iOS版的UIScrollView性能(iOS UIScrollView performance)

2019-07-05 02:26发布

我想提高我的滚动性能UIScrollView 。 我有很多的UIButtons它(他们可能是数百个):每个按钮有一个PNG图像设置为背景。

如果我尝试出现时加载整个滚动,它需要太多的时间。 搜索在网络上,我已经找到一种方法来优化它(装卸页面滚动 ),但有一个在滚动每次我需要加载一个新的页面稍微停顿。

你有什么建议,使之顺利滚动?

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

- (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;
}

Answer 1:

下面是一些建议:

1)首先,要明白scrollViewDidScroll:将不断得到调用,当用户滚动。 不只是一次每页 。 所以,我将确保你有一个确保参与您的装载真正的工作只有每页触发一次逻辑。

通常情况下,我会保持一类像伊娃 int lastPage 。 然后, scrollViewDidScroll:叫,我计算新的当前页面 。 只有当它从不同伊娃做我触发加载。 当然,你需要保存动态计算指数( currentPage在你的代码)在你的伊娃。

2)另一件事是,我尽量不要做的所有的紧张工作scrollViewDidScroll:方法。 我只触发它。

因此,举例来说,如果你把你最发布的代码,并把它放在一个名为方法loadAndReleasePages ,那么你可以在做这个scrollViewDidScroll:方法,会推迟执行,直到后scrollViewDidScroll:饰面:

- (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];
    }
}

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

3)最后,你可能要略有不同的算法来进行实验时,滚动视图实际上滚动远远不够,你要加载和发布内容计算。 您目前使用的:

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

它基于的方式将产生整数页码/运营商,以及floatint投作品。 这可能是罚款。 但是,你可能会发现,你想有一个稍微不同的算法,在一个稍微不同的点来触发加载。 例如,您可能希望触发内容加载的页面滚动了整整50%,从一个页面到下一个。 或者您可能希望当你几乎完全关闭的第一页(也许90%)只触发它。

我相信,一个滚动密集型应用程序其实我写的确实需要我调整的精确时刻的页面滚动的时候,我做了沉重的资源加载。 所以,我用了一个稍微不同的取整函数来确定何时当前页面发生了变化。

你可能会玩弄这一点。

编辑:看你的代码多一点之后,我也看到你正在做的工作是加载和缩放图像。 这实际上也是一个后台线程的候选人。 您可以加载UIImage从文件系统,并做你的比例,在后台线程,并使用GCD最终设置按钮的背景图像(所加载的图像),并改变其框后面的UI线程。

UIImage是安全的,因为iOS的4.0在后台线程中使用。



Answer 2:

不要碰一行代码,直到你已经成型。 Xcode中包括出于这样的目的优秀的工具。

  1. 首先,在Xcode中,请确保你正在建设一个真正的设备,而不是模拟器

  2. 在Xcode中,选择从产品菜单简介

  3. 一旦仪器打开后,可选择Core Animation的仪器

  4. 在您的应用程序,在滚动视图左右滚动你要找个人资料

你会看到实时FPS的顶部,并在底部,你会看到基于总时间用尽所有的功能和方法调用的崩溃。 开始向下钻取最高次,直到你打在自己的代码的方法。 命中命令+ E看到在右边,这将显示每个函数和方法的完整的堆栈跟踪叫你点击面板。

现在,所有你需要做的就是消除或优化,以最“昂贵”的功能和方法的调用,并验证您的更高的FPS。

这样,你就不会浪费时间来优化盲目的,可能使那些对性能没有实际效果的变化。


我的答案是真的提高滚动视图和表视图性能较普遍的办法。 为了解决您的一些特别关注的问题,我强烈建议看先进的滚动视图使用这种WWDC视频: https://developer.apple.com/videos/wwdc/2011/includes/advanced-scrollview-techniques.html#advanced-scrollview -techniques



Answer 3:

这很可能杀死你的表现该生产线是:

addButton.layer.cornerRadius = 10.0;

为什么? 原来的cornerRadius性能是可怕的! 把它拿出来...保证速度大大提高了。

编辑:这个答案总结了你应该做的事情很清楚。

https://stackoverflow.com/a/6254531/537213



Answer 4:

我最常用的解决方法是光栅化浏览次数:

_backgroundView.layer.shouldRasterize = YES;
_backgroundView.layer.rasterizationScale = [[UIScreen mainScreen] scale];

但是,它的工作原理不是在任何情况下..只要试试吧



文章来源: iOS UIScrollView performance