我试图提高我的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;
}发布于 2012-12-12 22:18:49
以下是一些建议:
1)首先要理解的是,在用户滚动时,scrollViewDidScroll:将不断被调用。不只是每页一次。因此,我将确保您有逻辑确保加载中涉及的实际工作只触发一次,每页只触发一次。
通常,我会保留一个像int lastPage这样的类。然后,当调用scrollViewDidScroll:时,我计算新的当前页面。只有当它与伊瓦尔不同时,我才会触发装载。当然,您需要将动态计算的索引(代码中的currentPage)保存在ivar中。
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版本开始使用的代码,因此您当然也可以用异步GCD方法调用替换performSelector:调用。重点不是在内部执行滚动视图委托回调。
3)最后,当滚动视图实际滚动到需要加载和发布内容的程度时,您可能需要尝试一些稍微不同的计算算法。您目前使用:
int currentPage=(int)(offset.y / 322.0f);它将根据/运算符和float到int转换的工作方式生成整数页码。那可能没问题。但是,您可能会发现,您需要一个稍微不同的算法,以便在稍微不同的点触发加载。例如,当页面从一个页面滚动到另一个页面时,您可能需要触发内容加载。或者你可能只想在你几乎完全离开第一页的时候才触发它(也许90%)。
我相信,我编写的一个滚动密集型应用程序确实需要我在页面滚动中精确地调整我在加载大量资源时的时间。因此,我使用了一个稍微不同的舍入函数来确定当前页面何时发生了更改。
你也可以玩这个。
编辑:在多看代码之后,我还看到您正在做的工作是加载和缩放图像。这实际上也是一个背景线程的候选人。您可以从文件系统中加载UIImage,并在后台线程上进行缩放,并使用GCD最终将按钮的背景图像设置为加载的图像,并在UI线程上更改其框架。
自UIImage 4.0以来,在后台线程中使用iOS是安全的。
发布于 2012-12-12 22:43:59
在您分析完之前,不要触及一行代码。 Xcode包含了用于此目的的优秀工具。
您将在顶部看到实时FPS,而在底部,您将看到基于运行总时间的所有函数和方法调用的分解。开始钻研最高的时间,直到您在自己的代码中命中方法。点击Command +E查看右侧的面板,它将显示每个函数和方法调用的完整堆栈跟踪。
现在,您所要做的就是消除或优化对最“昂贵”函数和方法的调用,并验证您的更高的FPS。
这样,您就不会浪费时间来优化盲目,并且可能会做出对性能没有真正影响的更改。
我的回答实际上是一种改进滚动视图和表视图性能的更普遍的方法。为了解决您特别关心的问题,我强烈建议您在高级滚动视图上观看这个WWDC视频:https://developer.apple.com/videos/wwdc/2011/includes/advanced-scrollview-techniques.html#advanced-scrollview-techniques。
发布于 2012-12-12 22:51:27
有可能会扼杀你的表演的台词是:
addButton.layer.cornerRadius=10.0;
为什么?原来cornerRadius的表现很糟糕!拿出来..。保证了巨大的加速。
编辑:这个答案很清楚地总结了你应该做的事情。
https://stackoverflow.com/questions/13849284
复制相似问题