UICollectionView性能 - _updateVisibleCellsNow(UICol

2019-09-01 17:52发布

我工作的一个自定义UICollectionViewLayout显示按天/周/月组织细胞。

它不是滚动光滑,它看起来像滞后所引起[UICollectionView _updateVisibleCellsNow]被称为每个渲染循环。

性能对于<30个项目确定,但在大约100个或更多,其非常缓慢。 这是UICollectionView和自定义布局的限制,还是我不给视图足够的信息来正确地执行?

这里来源: https://github.com/oskarhagberg/calendarcollection

布局: https://github.com/oskarhagberg/calendarcollection/blob/master/CalendarHeatMap/OHCalendarWeekLayout.m

数据源和委托: https://github.com/oskarhagberg/calendarcollection/blob/master/CalendarHeatMap/OHCalendarView.m

时间配置文件:

更新

也许它徒劳? 一些测试用纯UICollectionViewControllerUICollectionViewFlowLayout被给予大约的细胞/屏幕结果相同的量以类似的时间曲线。

我觉得它应该能够同时处理100〜简单不透明细胞无抖动。 我错了吗?

Answer 1:

也不要忘记尝试光栅化单元的层:

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

我有10 fps的不说,并与繁荣55fps! 我不是很熟悉,GPU和合成模式,所以它有什么作用恰恰是不完全清楚,我,但基本上拉平所有子视图的渲染,只有一个位图(而不是每个子视图一个位图?)。 反正我不知道是否有一些缺点,但它大大加快!



Answer 2:

我一直在做的UICollectionView性能相当的研究。 结论很简单。 表现为大量细胞的差。



编辑:抱歉,刚刚重新看了你的帖子,你有细胞的数量应行(见我的评论的其余部分),所以电池的复杂性也可能是一个问题。

如果您的设计支持它检查:

  1. 每个单元是不透明的。

  2. 单元格内容剪辑边界。

  3. 细胞的坐标位置不包含分数值(例如总是计算为整个像素)

  4. 尽量避免重叠细胞。

  5. 尽量避免阴影。



这样做的原因其实很简单。 很多人不明白这一点,但它是值得理解:UIScrollViews不使用的Core Animation滚动。 我天真地认为是他们参与代表们的一些秘密滚动动画“酱油”,只是要求不定期更新飘飞检查状态。 但事实上滚动意见不直接适用任何绘图行为的所有对象。 所有他们真的是应用数学函数抽象的坐标所包含的UIViews放置一类,因此视图坐标被视为相对于抽象的内容查看平面,而不是相对于包含视图的原点。 一种涡旋视图将更新抽象滚动面的与用户输入(例如挥动)一致的位置,当然是有物理算法以及其给出“momentumn”将翻译坐标位置。

现在,如果你要制作自己的集合视图布局对象,从理论上讲,你可以产生一个其100%逆转由底层滚动型应用数学翻译。 这将是有趣的,但没用,因为它会出现在屏幕上的细胞并不因为你刷卡移动。 但我提出这种可能性,因为它说明了集合视图布局对象与集合视图对象本身的表现非常类似的操作底层滚动视图工作。 例如,它只是提供要显示的机会,通过意见的属性框转换申请额外的数学框架,并在主,这将是简单的位置属性的转换。

只有当新的细胞被插入或删除移动或重载入CoreAnimation参与所有; 通常最致电:

- (void)performBatchUpdates:(void (^)(void))updates
                 completion:(void (^)(BOOL finished))completion

UICollectionView请求细胞layoutAttributes用于滚动的每个帧和每个可见视图是针对每一帧布局。 的UIView的是灵活性比性能更优化的丰富的对象。 每当一个被摆出来,有一些测试系统并检查它的α,zIndex的,子视图,剪辑属性等名单很长。 这些检查和由此产生的任何变化的视图被用于每个帧中的每个集合视图项目进行。

为了保证良好的性能通过帧的动作所有的帧需要被17MS内完成。 [有了你,那简直是不可能发生的细胞数量]括号内的这一条款,因为我重新看了你的帖子,我意识到我看错了。 你所拥有的细胞数量,不应该有一个性能问题。 我本人与香草细胞的简化试验中发现,只包含单个缓存图像,在iPad 3测试的极限为约784屏幕上的细胞性能开始下降到低于50fps的之前。

在实践中,你需要保持低于这一标准。

我个人用我自己的自定义布局对象,并需要比UICollectionView提供更高的性能。 不幸的是我没有跑,直到上面的一些话了发展道路的简单的测试,我意识到有性能问题。 我所以我将要返工开源UICollectionView,PSTCollectionView回港。 我认为是可以实现的话,只是一下,每个单元项目的层使用规避UIView的每一个单元的布局的操作写入的总体滚动一种解决方法。 这会为我工作,因为我有自己的布局对象,我知道当需要布局和我有一个绝招,将允许PSTCollectionView此时回落到其正常的运作模式。 我检查调用序列,并没有显得过于复杂,也不会出现在所有的不可行。 但可以肯定的是不平凡和一些进一步的测试已经做之前,我可以确认它会奏效。



Answer 3:

一些更多的观测可能会有所帮助:

我能够重现问题,使用流布局与周围的175项可见一次的:它顺利地滚动在模拟器上,但落后于像iPhone 5地狱确信他们是不透明等。

什么最终花费大部分时间似乎与内可变的NSDictionary工作_updateVisibleCellsNow 。 这两个复制的字典,也受到键查找项目。 键似乎是UICollectionViewItemKey和[UICollectionViewItemKey isEqual:]的方法是所有的最耗时的方法。 UICollectionViewItemKey含有至少typeidentifierindexPath属性,以及包含的属性indexPath比较[NSIndexPath isEqual:]采用最多的时间。

从我猜可能是因为缺乏的UICollectionViewItemKey哈希函数isEqual:的字典查找期间经常调用。 许多项目可能会结束了相同的哈希(或相同的散列桶,不知道如何NSDictionary的作品)。

出于某种原因,它是用1节的所有项目更快,相比于在每个7个项目的许多部分。 可能是因为它花费在NSIndexPath的isEqual这么多的时间,这是更快,如果该行的diff第一,或者也许是UICollectionViewItemKey得到一个更好的哈希值。

老实说,那感觉真是奇怪的是UICollectionView确实是沉重的字典工作的每滚动框架,如之前提到的每一帧更新必须<16毫秒,以避免滞后。 我不知道这许多字典查找或者是:

  • 真的有必要为一般UICollectionView操作
  • 有支持一些边缘情况很少使用,可以关闭大部分布局
  • 这还没有固定的一些未经优化的内部代码
  • 从我们身边的一些错误

希望我们会看到一些改善这个夏天WWDC期间,或如果别人在这里能弄清楚如何解决它。



Answer 4:

问题不在于你是在收集视图显示总细胞数,它是在屏幕上立刻细胞的数量。 由于电池体积非常小巧(22×22),你有154层的细胞显示在屏幕上一次。 每个渲染这些是什么减慢你的界面了。 你可以在你的故事板,并重新运行该应用程序增加了单元尺寸证明了这一点。

不幸的是,有没有什么可以做。 我建议避免裁剪到边界,并试图不落实减轻问题drawRect:因为它的速度慢。



Answer 5:

大大拇指两个以上的答案! 这里是你可以尝试一个额外的东西:我已经在大的改进UICollectionView通过禁用自动布局的表现。 当你将需要添加一些额外的代码来布局单元格的室内设计,自定义代码似乎比大大自动布局更快。



Answer 6:

这里是Altimac的回答转化为斯威夫特3:

cell.layer.shouldRasterize = true
cell.layer.rasterizationScale = UIScreen.main.scale

此外,应该注意的是,这个代码放在为您cellForItemAtIndexPath委托的CollectionView方法。

一个更尖 - 看每秒(FPS)的应用程序的框架,开拓下仪器核心动画(参照图像)。



Answer 7:

在少数情况下,它是由于UICollectionViewCell自动布局。 关掉它(如果你能活着没有它)和滚动会变得光滑黄油:)这是一个iOS的问题,他们havnt从年龄段解决。



Answer 8:

就像@Altimac,我也用这个代码来解决与iPad上UICollectionView滚动一个问题:

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

我也有6-10fps,我现在有57fps



Answer 9:

如果要实现网格布局,可以解决此通过使用单一UICollectionViewCell每个row并添加嵌套UIView的的cell 。 其实,我子类UICollectionViewCellUICollectionReusableView并推翻了prepareForReuse方法删除所有子视图。 在collectionView:cellForItemAtIndexPath:我加入所有的subviews最初是细胞设置其帧到x原实坐标使用,但调整它的y座标为内部cell 。 使用这种方法我能仍然使用一些的细微的UICollectionViewtargetContentOffsetForProposedContentOffset:withScrollingVelocity:对电池的顶部和左侧很好地对齐。 我从获得4-6 FPS的流畅的60帧去。



Answer 10:

除了以上答案(光栅化,自动布局,等等),你可能还需要检查潜在拖累业绩等原因。

就我而言,我的每一个UICollectionViewCell包含另一个UICollectionView(每个约25个细胞),各小区中加载的时候,我打电话内UICollectionView.reloadData(),这显著拖累性能。

然后我把reloadData主UI队列里面,这个问题消失了:

DispatchQueue.main.async {
    self.innerCollectionView.reloadData()
}

仔细观看的理由喜欢它们可能会有所帮助。 谢谢! :)



Answer 11:

我以为我会很快给我的解决方案,因为我面临着一个非常类似的问题 - 基于图像的UICollectionView。

在项目我工作中,我通过网络获取图像,本地缓存它在设备上,然后重新加载在滚动过程中缓存的图像。

我的缺点是,我没有在后台线程加载缓存图片。

有一次,我也把我的[UIImage imageWithContentsOfFile:imageLocation]; 进入后台线程(,然后通过我的主线程它应用到我的ImageView的),我的FPS和滚动是好多了。

如果你还没有尝试过,肯定会给一展身手。



文章来源: UICollectionView performance - _updateVisibleCellsNow