Swift 中的 NSAttributedString draw(in: rect) 性能不佳?

Posted

技术标签:

【中文标题】Swift 中的 NSAttributedString draw(in: rect) 性能不佳?【英文标题】:NSAttributedString draw(in: rect) in Swift have poor performance? 【发布时间】:2018-12-01 07:44:38 【问题描述】:

我有一个UIView,它可能同时在屏幕上包含超过 400-500 个字符串。而这个UIView 是可滚动的。第一个决定是使用带有自定义布局的UICollectionView。当UIcollectionView 中的项目同时在屏幕上不超过 30-50 时,它的效果很好。如果我挤压UIView(通过捏合手势)并且它同时在屏幕上显示超过 100-150 个项目,我在尝试滚动时会有延迟(帧速率下降并且绘制 1 帧需要超过 20-50 毫秒) .

好的。然后我决定完全手动绘制UIView。对于绘制形状,我使用 CGContext(即使我同时绘制 1000-2000 个形状也能很好地工作)。但是当我尝试使用 NSAttributedString draw(in: rect) 绘制许多字符串(100-300)时,它在滚动时的性能非常差。

绘制 50 个字符串(帧率还可以,绘制时间接近 16ms)

绘制 150 个字符串(帧率很差,接近 30ms 用于绘制)

绘制 300 个字符串(帧率很差,接近 60ms 用于绘制)

所以,问题是 为什么 即使我同时将 300 UILabel 添加到 View 并尝试滚动其帧速率也可以(不是更多每帧 17 毫秒)。 UILabel 如何在屏幕重绘时在内部绘制文本以获得高性能?

【问题讨论】:

【参考方案1】:

在大多数情况下,自己绘制并不能提高性能。

UILabel 速度很快,因为它必须只绘制一次字符串。然后它将绘制的字符串的位图图像保存在其后备存储中。这将在之后的每一帧中重复使用。此外,所有视图位图的合成都将通过 GPU 完成。

如果您自己进行绘图,您很可能无法从缓存的后备存储中受益。正如您刚刚注意到的那样,必须重绘整个视图很慢。可以通过只重绘实际改变的部分来改进(使用setNeedsDisplay(rect)),但是在缩放时这通常是不可能的。手动绘图很慢,因为您用于此操作的所有核心图形 API 都在 CPU 上运行,而不是 GPU。

您使用带有自定义布局的集合视图的方法应该能够很好地处理这个问题。编写一个性能良好的集合视图布局并不容易,但它是可能的。也许你应该用仪器调查一下,然后再问另一个问题。

【讨论】:

以上是关于Swift 中的 NSAttributedString draw(in: rect) 性能不佳?的主要内容,如果未能解决你的问题,请参考以下文章

swift中的mutating

Swift 4 或 Swift 5 中的 UITextView 用户输入文本验证:限制 Swift 中的特定字符

swift: Swift 中的 SnapKit 不起作用

swift中的 isKindofClass

swift Swift中的变量

swift Swift中的数组