swift - 在 UITableViewCell 内执行耗时的任务,滚动时暂停

标签 swift uitableview asynchronous prefetch

我有 TableView 和代表事件的海关单元格。它看起来与这里的第一张和第三张图片非常接近。

enter image description here

正如您所看到的(抱歉,分辨率太小),在某些事件中,有即将参加的 friend 的照片。 不幸的是,有关 friend 的信息并未加载有关事件的其他信息。

因此,在获得事件列表后,我可以请求加载将参加每个事件的 friend 列表。

现在我使用类似的代码

class EventCell : UITableViewCell {
  var eventForCell : Event? {
      didSet {
          eventTitleLabel.text = eventForCell.title
          eventDateLabel.text = eventForCell.date
          presentFriends(eventID : eventForCell.id)
      }
   }

   func presentFriends(eventID : Int) {
        //searching for friends for event with specific eventID
        .......
        .......

        //for every friend found adding UIImageView in cell
        for friend in friendsOnEvent {
          let avatar = UIImageView(....)
          self.addSubview(avatar)
        }
   }
}

此代码有效,但照片无法顺利呈现。此外,如果您快速滚动列表,它们就会开始闪烁。如果用户快速滚动事件列表,甚至可能不需要加载它们。所以我有两个问题:

  1. 考虑到为每个事件呈现 friend 可能需要时间,并且有时在单元格滚动离开后完成,我如何才能获得流畅的滚动体验。
  2. 如果我已经加载了事件列表并且已经将它们呈现在单元格中。在获得要参加的 friend 的信息后,如何更新这些单元格?
  3. 当用户滚动并且我正在创建异步任务以在单元格中显示一些图像时,我认为我应该使用对自身的弱引用,并且可能检查它不等于 nil,这样如果单元格现在不可见,任务将被取消。应该怎么做?

更新:

我在 UITableViewPrefetchingDataSource 协议(protocol)中找到了有关 tableView(_:prefetchRowsAt:) 方法的信息,我应该在这种情况下使用它吗?也许有人有这方面的经验?

最佳答案

1.cellForRowAt 期间(重新)创建 View 对象通常是一种不好的做法。从屏幕截图中,我假设单个单元格上的头像数量是有限的 - 我建议在单元格初始值设定项中创建所有 UIImageView 对象,然后在 presentFriends 中创建code> 只需为它们设置图像,然后隐藏未使用的图像 (isHidden = true) 或将其 alpha 设置为 0(当然,不要忘记取消隐藏已使用的图像)。

2. 如果您使用 SDWebImage 加载图像,请实现 prepareForReuse 并取消当前下载,以在滚动期间获得一点性能提升,并防止未定义的行为(当您尝试在上一张图像尚未下载时设置另一张图像)。基于this question , this onethis one我期望类似的东西:

    override func prepareForReuse() {
        super.prepareForReuse()
    
        self.imageView.sd_cancelCurrentImageLoad()
    }

 Or you can use [this gist][4] for an inspiration.

P.S.:此外,您必须眨眼数数,因为图像是从网络下载的 - 总会有一些延迟。通过缓存,您可以立即获取已下载的图像,但新的图像将会有延迟 - 没有办法阻止这种情况(除非您在呈现 tableView 之前预加载所有可以出现在 tableView 中的图像)。

P.S.2:您可以尝试使用[UITableViewDataSourcePrefetching][6]来实现预取。这可以帮助您解决因从网络下载头像而导致的眨眼问题。这会让事情变得更复杂,但如果你真的想消除闪烁,你就必须亲自动手。

首先,正如您从文档中看到的,prefetchRowsAt 不会为您提供单元对象 - 因此您必须将图像预取到模型对象,而不是简单地使用 sd_setImage 位于给定单元格的 UIImageView 对象上。也许是前面提到的gist将帮助您下载图像到模型。

现在也为documentation状态:

Loading Data Asynchronously

The tableView(_:prefetchRowsAt:) method is not necessarily called for every cell in the table view. Your implementation of tableView(_:cellForRowAt:) must therefore be able to cope with the following potential situations:

Data has been loaded via the prefetch request, and is ready to be displayed.

Data is currently being prefetched, but is not yet available.

Data has not yet been requested.

简单地说,当您处于 cellForRowAt 中时,您不能依赖 prefetchRowsAt 被调用 - 它可能已被调用,也可能没有被调用(或者可能通话正在进行中)。

文档继续建议使用 Operation 作为下载资源的支持工具 - prefetchRowsAt 可以创建负责下载的 Operation 对象给定行的头像。然后,cellForRowAt 可以向模型询问给定行的 Operation 对象 - 如果没有任何对象,则意味着未调用 prefetchRowsAt该行,您必须在此时创建 Operation 对象。否则,您可以使用 prefetchRowsAt 创建的操作。

无论如何,如果你认为值得,你可以使用这个 tutorial作为实现预取的灵感。

关于swift - 在 UITableViewCell 内执行耗时的任务,滚动时暂停,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49346881/

相关文章:

IOS TableView 问题

python - 如何从 Flask 服务器发送多个异步请求?

ios - 变量/常量声明差异

objective-c - 制作一个 float 只显示小数点前的 x 位

ios - 从初始化程序返回而不初始化所有存储的属性swift ios

ios - (Swift) Tableview 单元格 : textLabel showing up, 但 detailTextLabel 不是

swift - 如何在 SceneKit 中加载 Gigapixel 图像作为 Material ?

ios - UITableViewCell 覆盖向左滑动的删除按钮

c# - 如何通过对原始序列的值运行任务来创建 Rx 序列?

linux - 如何调试 tokio 任务挂起的位置?