我正在尝试提高 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;
}
最佳答案
这里有一些建议:
1) 首先,了解 scrollViewDidScroll:
将在用户滚动时连续调用。不仅仅是每页一次。所以,我会确保你有逻辑来确保加载中涉及的实际工作每页只触发一次 。
通常,我会保留一个类 ivar 像 int lastPage
。然后,当 scrollViewDidScroll:
被调用时,我计算新的当前页面。只有当它与 ivar 不同时,我才会触发加载。当然,那么您需要将动态计算的索引(代码中的 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:
调用。重点不是在 滚动 View 委托(delegate)回调中执行 。3) 最后,您可能想尝试使用稍微不同的算法来计算滚动 View 何时实际滚动到您想要加载和释放内容的程度。您目前使用:
int currentPage=(int)(offset.y / 322.0f);
这将根据
/
运算符和 float
到 int
转换的工作方式产生整数页码。那可能没问题。但是,您可能会发现需要稍微不同的算法,以在稍微不同的点触发加载。例如,您可能希望在页面从一页滚动到下一页时触发内容加载。或者您可能只想在您几乎完全离开第一页(可能 90%)时触发它。我相信我编写的一个滚动密集型应用程序确实需要我在加载大量资源时调整页面滚动中的精确时刻。因此,我使用了稍微不同的舍入函数来确定当前页面何时发生更改。
你也可以玩这个。
编辑: 在查看您的代码之后,我还看到您正在做的工作是加载和缩放图像。这实际上也是后台线程的候选者。您可以从文件系统加载
UIImage
,并在后台线程上进行缩放,并使用 GCD 最终设置按钮的背景图像(为加载的图像)并将其框架更改回 UI 线程。从 iOS 4.0 开始,
UIImage
可以安全地在后台线程中使用。关于iOS UIScrollView 性能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13849284/