【问题标题】:Load all cell content asynchronously异步加载所有单元格内容
【发布时间】:2017-06-16 23:40:18
【问题描述】:

关于如何异步加载图像和其他下载的数据有很多问题,但没有关于将这些图像(一旦下载)异步加载到 UI 中的问题。

我正在尝试在 UICollectionView 上实现 60 fps 的滚动,尽管我正在寻找适用于所有视图的解决方案。

例如,如果我想将 Assets 文件夹中的图像加载到快速滚动的 UIScrollView 中,则将它们加载到内存中然后在 UI 中绘制需要一些时间。有什么方法可以先将这些图像缓存在内存中(同时不会损害内存/使用太多),然后在将它们缓存在内存中后将它们加载到单元中?

按照我现在的理解,当我说,

cell.imageView.image = UIImage(named: "grapes")

assets 文件夹grapes 被打开并从中读取,然后将相应的图像(1x、2x 或 3x)带入内存。将正确的图像带入内存后,将cell.imageView.image分配给它,然后绘制图像。这是准确的吗?所有这些都发生在主线程上吗?如果是这样,那么这不是有可能阻塞主线程并导致设备跳过一些帧吗?

我假设在解决复杂表达式或格式化文本时会发生类似的过程。如果是这样,那么我可以以及如何创建要异步加载的内容,以便主线程不会被阻塞(即使以暂时空的内容视图为代价)?

【问题讨论】:

  • @Rob,“图像的尺寸不适合 UI”,您的意思是 UIImageView 需要执行更多操作以进行缩放或其他操作吗?
  • 我说的是图像视图,例如 40x40 点,但您使用的是 1200x1200 像素的图像,并且您使用的内容模式为 .scaleAspectFit.scaleAspectFill。真的是任何你依赖contentMode 来适当缩放图像的情况。或者,显然,如果您正在调用自己的缩放/裁剪例程。

标签: ios swift asynchronous


【解决方案1】:

是的,将图像加载到内存可能需要几毫秒的时间。如果图像的大小不适合 UI(例如,您依靠 contentModescaleAspectFit.scaleAspectFill 来调整图像大小;或者如果您正在手动调整大小,则它尤其会影响体验/自己裁剪图像)。这可以相当无害地导致计算复杂的东西。

一般来说,有问题还不够,但它会阻止您达到最佳 60 fps。如果您想要获得最佳性能,您可以进行异步检索,将其与 table view prefetchingcollection view prefetching 结合使用。

对于复杂的表达式和文本格式,不太可能引入足够的延迟来阻止 60 fps。但不要猜测(re this or images)。您应该通过 Instruments 分析“发布”构建(启用优化)的 fps,并查看其中哪些(如果有的话)对 fps 有明显的影响。并在您的目标物理设备上进行测试,而不是在模拟器上进行测试,因为过早地优化物理设备上不会出现的问题会浪费时间。

【讨论】:

  • 单元格中的简单 UIView 动画是否也会降低帧速率?
  • 我建议您停止猜测可能会影响 fps 的因素,并凭经验验证(通过优化的发布版本)实际执行的操作。 “过早的优化是万恶之源。但我们不应该放弃那关键的 3% 的机会。”在你为此失去太多睡眠之前,请确保你处于那 3% 中。但是在回答您的问题时,不,简单的动画可能不会对 fps 产生不利影响,尽管复杂的动画(例如,使用高斯模糊/阴影对复杂的视图层次结构进行动画处理可能会很好)。
【解决方案2】:

在背景上创建图像,在 UI 线程上设置:

DispatchQueue.global(qos: .background).async {
    let image = UIImage(named: "image")

    DispatchQueue.main.async {
        cell.imageView.image = image
    }
}

这是简化的示例,您需要注意滚动和单元格重用。

【讨论】:

  • 我认为所有改变 UI 的事情都需要在主线程上发生?
  • @IHaveAQuestion 这不是 UI,你不应该在后台线程上更改 UI 树视图,但你可以在那里加载任何你想要的东西
  • 太棒了,谢谢!另外,你为什么把所有东西都放在那个街区里?也就是.global(qos: .background).async.main.async有什么区别?
  • 对不起,如果这个问题看起来很傻,但是:所以global(qos: .background).async在后台线程上运行,而main.async在主线程上运行,而主线程不是UI线程?
  • 是的,所以在后台线程中检索图像,然后在主线程(这是所有 UI 更新的线程)上更新 imageview。仅供参考,如果用于预取,我会使用 .background。但是,如果您要在 UI 中获取即时演示,您可能会使用 .userInitiated.userInteractive
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多