如何提高iPhone UITableView滚动性能?

89

我有一个uitableview,在每个单元格中加载相当大的图像,而单元格的高度取决于图像的大小。滚动性能还不错,但有时会有些卡顿。

我在FieryRobot博客上找到了以下这些提示:

带有uitableview的流畅滚动

更多uitableview的流畅滚动

是否有人有改善uitableview滚动性能的提示?


如果您需要缓存单元格高度(这可能是昂贵的计算,并且经常使用),我已经给出了一个示例。只有在您的应用程序中适用时才使用它。 - Paul de Lange
5个回答

157
  1. 缓存行高(表格视图可能经常请求此项)
  2. 为表格中使用的图像创建一个最近最少使用缓存(并在收到内存警告时使所有非活动条目失效)
  3. 尽可能在UITableViewCelldrawRect:中绘制所有内容,如果需要标准的可访问性功能,则使用内容视图的drawRect:,而要尽可能避免使用子视图。
  4. 使你的UITableViewCell的层不透明(如果有内容视图也同样如此)
  5. 根据UITableView示例/文档建议使用可重用单元格标识符功能
  6. 避免使用未预先制作为UIImage的渐变/复杂图形效果

5
在将图片显示在单元格上之前,应将下载的图像缩小到imageView的大小! - Zoltán Matók
4
我想在这个回答中补充一些我过去几年的经验- 透明单元格可能从来不是造成滚动性能差的原因。我们有一个非常复杂的应用程序(20多个子视图)并且全部都是透明的以显示背景。通过适当的优化,即使在3GS上,透明度也不会产生影响。实际上最拖慢应用程序的事情是在足够的单元格可以从表视图中出列之前加载nib文件。如果使用子视图,请确保具有有效的层次结构,您不需要使用drawRect方法。 - Accatyyc
@Accatyyc,看起来我也有同样的问题。当没有足够的单元格可出列时,会有一小段延迟,当出列3-4个单元格时,滚动是平滑的。有没有办法预加载单元格,这样就有单元格可出列而不需要在滚动时加载NIB文件? - Tiois
@Tiois 当然有办法。您需要使用旧的单元格出列方式(不要注册类/ Nib,而是在dequeueCellWithIdentifier:返回nil时创建它们)。这样,您可以在表视图甚至存在之前创建一组单元格,例如在之前创建20个单元格。然后,在cellForRowAtIndexPath:中不再创建新的单元格,而是首先从自己的缓存中提取它们,直到缓存为空为止。 - Accatyyc
我正在使用UICollectionView并遇到相同的问题。在我的自定义单元格nib文件中,我使用了几个子视图,包括UIwebView和UIImageView,它们都在垂直的UIstackView内部。当我滚动列表时,重用的单元格需要显著的时间来重新调整它们的大小,滚动看起来非常卡顿。 - Mansuu....
我也遇到了同样的问题,使用UITableView并根据需要显示的子视图数量计算每个单元格的高度。每个单元格都有多行文本标签、UIButtons和一个用于图像和视频的集合视图,也可能是一个MapView。滚动不会卡顿,但有时在添加更多行时会冻结。 - Manish Nahar

40
  1. 如果您正在子类化UITableViewCell,请勿使用Nib,而是在代码中编写。这比加载Nib文件要快得多。
  2. 如果您正在使用图像,请确保缓存它们,以便每个图像只需从文件加载一次(如果您有足够的内存-您会惊讶于图像占用多少空间)。
  3. 尽可能使尽可能多的元素不透明。同样,尽量不要使用带有透明度的图像。

3
不用担心,那些都是巨魔。回答得很好! - Steav
79
可能有人对"避免使用Nib文件"这个建议持反对意见,因为这并不能提高性能。如果你正在重复利用单元格,在滚动时,这些单元格根本不会从Nib文件中重构。 - Steven Fisher
6
根据《Cocoa With Love》的研究,Nib文件在速度上与手动构建视图相当,甚至略快一些。http://www.cocoawithlove.com/2010/03/load-from-nib-or-construct-views-in.html - MaxGabriel
@Steven Fisher 使用Nib可能会在表格分配和释放单元格时变慢,例如-在快速滚动期间。 - Sound Blaster
3
在单元格正在构建时使用 NIBs 可能会更慢。如果您正在使用单元格回收,那么只有足够填满屏幕的一定数量的单元格被分配,最多可能是10个。当您滚动时,不会创建单元格,它们只是被重用,而不是被释放和重新分配。当然,这样就没有释放内存的成本了。因此,不,这是不正确的。 - Steven Fisher

34
Tweetie的开发者曾经详细地写过如何实现这一点,并提供了一些代码示例。基本上,他/她主张在每个表格单元中使用一个自定义视图,并手动绘制它(而不是使用接口生成子视图等其他选项)。 fast-scrolling-in-tweetie-with-uitableview 此外,苹果公司已经更新了其TableViewSuite教程中TableView的示例代码(也许是为响应这个建议?)。 TableViewSuite

1
这是一个很棒的解决方案。我只是好奇如何将UIButton添加到cellView中?它是在drawRect方法中绘制的吗? - Sukitha Udugamasooriya
1
@beno,你的链接好像有问题(第一个链接),有没有可能让我们得到原始文章? - apouche
3
原文链接:http://web.archive.org/web/20100922230053/http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/ - jverdi
已添加网络存档链接,因为原始版本不存在。 - Ralph Willgoss

1
UITableView滚动的#1性能杀手是在任何单元格视图层上绘制阴影,因此如果滚动性能很重要,则不要添加阴影,除非基本上不会减慢主线程。由于没有任何答案提到阴影和层,所以认为这必须被说出来。:+)

6
如果问题是阴影,添加这两行代码就可以完美解决:self.layer.shouldRasterize = YES; self.layer.rasterizationScale = UIScreen.mainScreen.scale; - Pedro Romão

0

在使用UITableView滚动性能方面遇到的任何问题,都可以使用其他答案中已经描述的技术来解决。但是,许多时候,不良的性能是由于某些固有错误或重复引起的。

UITableView重用单元格,每个单元格可能需要自己的图像 - 这使得解决方案变得有点复杂。从解决问题的常规方法来看,我在这里总结了一些需要注意的事项:

  1. 将数据从REST /数据库加载到数据源中。此步骤应在后台完成,最终使用dispatch_async和GCD队列。
  2. 创建和初始化相关数据模型对象,并将它们放置在数组中
  3. [tableView reloaddata]
  4. cellForRowAtIndexPath内,包括将数据(文本)从数组的正确数据模型对象中设置的代码。
  5. 现在,图像也可能以URL形式存在,因此由表视图执行单元格重用,所以这一步可能有点奇怪。其关键在于再次使用异步队列从设备缓存/ URL加载图像,然后将其设置为正确的cell.image(无论您的单元格图像属性如何)。
为了避免问题,在表视图中使用图片的懒加载,请参考本教程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接