iOS UIScrollView性能 [英] iOS UIScrollView performance

查看:107
本文介绍了iOS UIScrollView性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试提高 UIScrollView 的滚动性能。我上面有很多 UIButtons (它们可能是数百个):每个按钮都有 png 图像设置为背景。

I'm trying to increase the scrolling performance of my UIScrollView. I have a lot of UIButtons on it (they could be hundreds): every button has a png image set as background.

如果我尝试在出现时加载整个卷轴,则需要花费太多时间。在网上搜索,我找到了一种优化它的方法(在滚动时加载和卸载页面),但每次我必须加载新页面时滚动有一点暂停。

If I try to load the entire scroll when it appears, it takes too much time. Searching on the web, I've found a way to optimize it (loading and unloading pages while scrolling), but there's a little pause in scrolling everytime I have to load a new page.

你有什么建议让它顺利滚动吗?

Do you have any advice to make it scroll smoothly?

下面你可以找到我的代码。

Below you can find my code.

- (void)scrollViewDidScroll:(UIScrollView *)tmpScrollView {
    CGPoint offset = tmpScrollView.contentOffset;
    //322 is the height of 2*2 buttons (a page for me)
    int currentPage=(int)(offset.y / 322.0f);
    if(lastContentOffset>offset.y){
        pageToRemove = currentPage+3;
        pageToAdd = currentPage-3;
    }
    else{
        pageToRemove = currentPage-3;
        pageToAdd = currentPage+3;
    }
    //remove the buttons outside the range of the visible pages
    if(pageToRemove>=0 && pageToRemove<=numberOfPages && currentPage<=numberOfPages){
        for (UIView *view in scrollView.subviews)
        {
            if ([view isKindOfClass:[UIButton class]]){
                if(lastContentOffset<offset.y && view.frame.origin.y<pageToRemove*322){
                    [view removeFromSuperview];
                }
                else if(lastContentOffset>offset.y && view.frame.origin.y>pageToRemove*322){
                    [view removeFromSuperview];
                }
            }
        }
    }
    if(((lastContentOffset<offset.y && lastPageToAdd+1==pageToAdd) || (lastContentOffset>offset.y && lastPageToAdd-1==pageToAdd)) && pageToAdd>=0 && pageToAdd<=numberOfPages){
        int tmpPage=0;
        if((lastContentOffset<offset.y && lastPageToAdd+1==pageToAdd)){
            tmpPage=pageToAdd-1;
        }
        else{
            tmpPage=pageToAdd;
        }
        //the images are inside the application folder
        NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
        for(int i=0;i<4;i++){
            UIButton* addButton=[[UIButton alloc] init];
            addButton.layer.cornerRadius=10.0;
            if(i + (tmpPage*4)<[imagesCatalogList count]){
                UIImage* image=[UIImage imageWithContentsOfFile:[NSString stringWithFormat: @"%@/%@",docDir,[imagesCatalogList objectAtIndex:i + (tmpPage*4)]]];
                if(image.size.width>image.size.height){
                    image=[image scaleToSize:CGSizeMake(image.size.width/(image.size.height/200), 200.0)];
                    CGImageRef ref = CGImageCreateWithImageInRect(image.CGImage, CGRectMake((image.size.width-159.5)/2,(image.size.height-159.5)/2, 159.5, 159.5));
                    image = [UIImage imageWithCGImage:ref];
                }
                else if(image.size.width<image.size.height){
                    image=[image scaleToSize:CGSizeMake(200.0, image.size.height/(image.size.width/200))];
                    CGImageRef ref = CGImageCreateWithImageInRect(image.CGImage, CGRectMake((image.size.width-159.5)/2, (image.size.height-159.5)/2, 159.5, 159.5));
                    image = [UIImage imageWithCGImage:ref];
                }
                else{
                    image=[image scaleToSize:CGSizeMake(159.5, 159.5)];
                }


                [addButton setBackgroundImage:image forState:UIControlStateNormal];
                image=nil;
                addButton.frame=CGRectMake(width, height, 159.5, 159.5);
                NSLog(@"width %i height %i", width, height);
                addButton.tag=i + (tmpPage*4);
                [addButton addTarget:self action:@selector(modifyImage:) forControlEvents:UIControlEventTouchUpInside];
                [tmpScrollView addSubview:addButton];
                addButton=nil;
                photos++;
            }
        }
    }
    lastPageToAdd=pageToAdd;
    lastContentOffset=offset.y;
}


推荐答案

以下是一些建议:

1)首先,了解 scrollViewDidScroll:将作为用户连续调用滚动。不只是每一次。因此,我会确保您拥有逻辑,确保您的加载中涉及的实际工作仅在每页一次时触发。

1) First, understand that scrollViewDidScroll: will get called continuously, as the user scrolls. Not just once per page. So, I would make sure that you have logic that ensures that the real work involved in your loading is only triggered once per page.

通常,我将保留一个类 ivar ,如 int lastPage 。然后,当调用 scrollViewDidScroll:时,我计算新的当前页面。只有当它与ivar不同时才会触发加载。当然,那么你需要在你的ivar中保存动态计算的索引(代码中的 currentPage )。

Typically, I will keep a class ivar like int lastPage. Then, as scrollViewDidScroll: is called, I calculate the new current page. Only if it differs from the ivar do I trigger loading. Of course, then you need to save the dynamically calculated index (currentPage in your code) in your ivar.

2)另一件事是我尝试不在 scrollViewDidScroll:方法中进行所有密集工作。我只在那里触发

2) The other thing is that I try not to do all the intensive work in the scrollViewDidScroll: method. I only trigger it there.

所以,例如,如果你把你发布的大部分代码放在一个名为<的方法中code> loadAndReleasePages ,然后你可以在 scrollViewDidScroll:方法中执行此操作,该方法将执行推迟到之后scrollViewDidScroll:结束:

So, for example, if you take most of the code you posted and put it in a method called loadAndReleasePages, then you could do this in the scrollViewDidScroll: method, which defers the execution until after scrollViewDidScroll: finishes:

- (void)scrollViewDidScroll:(UIScrollView *)tmpScrollView {
    CGPoint offset = tmpScrollView.contentOffset;
    //322 is the height of 2*2 buttons (a page for me)
    int currentPage = (int)(offset.y / 322.0f);

    if (currentPage != lastPage) {
        lastPage = currentPage;
        // we've changed pages, so load and release new content ...
        // defer execution to keep scrolling responsive
        [self performSelector: @selector(loadAndReleasePages) withObject: nil afterDelay:0];
    }
}

这是我早期使用的代码版本,所以你当然可以用异步GCD方法调用替换 performSelector:调用。关键是不要在内部滚动视图委托回调。

This is code that I've used since early iOS versions, so you can certainly replace the performSelector: call with an asynchronous GCD method call, too. The point is not to do it inside the scroll view delegate callback.

3)最后,你可能想要尝试稍微不同的算法来计算滚动视图何时实际滚动到足以让您加载和释放内容的时间。您目前使用:

3) Finally, you might want to experiment with slightly different algorithms for calculating when the scroll view has actually scrolled far enough that you want to load and release content. You currently use:

int currentPage=(int)(offset.y / 322.0f);

这将根据 / 运算符, float int 强制作品。那可能没事。但是,您可能会发现需要稍微不同的算法,以稍微不同的点触发加载。例如,您可能希望触发内容加载,因为页面已从一个页面滚动到下一个页面的50%。或者你可能只想在你几乎完全脱离第一页(可能是90%)时触发它。

which will yield integer page numbers based on the way the / operator, and the float to int cast works. That may be fine. However, you might find that you want a slightly different algorithm, to trigger the loading at a slightly different point. For example, you might want to trigger the content load as the page has scrolled exactly 50% from one page to the next. Or you might want to trigger it only when you're almost completely off the first page (maybe 90%).

我相信我写的一个滚动密集型应用程序实际上做了要求我在进行大量资源加载时调整页面滚动中的精确时刻。所以,我使用了稍微不同的舍入函数来确定当前页面何时发生了变化。

I believe that one scrolling intensive app I wrote actually did require me to tune the precise moment in the page scroll when I did the heavy resource loading. So, I used a slightly different rounding function to determine when the current page has changed.

你也可以玩这个。

编辑:在你的代码中,我还看到你正在做的 work 正在加载和缩放图像。这实际上也是后台线程的候选者。您可以从文件系统加载 UIImage ,并在后台线程上进行缩放,并使用GCD最终设置按钮的背景图像(到加载的图像)并更改它的框架返回UI线程。

after looking at your code a little more, I also see that the work you're doing is loading and scaling images. This is actually also a candidate for a background thread. You can load the UIImage from the filesystem, and do your scaling, on the background thread, and use GCD to finally set the button's background image (to the loaded image) and change its frame back on the UI thread.

UIImage 从iOS 4.0开始在后台线程中使用是安全的。

UIImage is safe to use in background threads since iOS 4.0.

这篇关于iOS UIScrollView性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆