我正在尝试在我的 collectionview 单元格中显示下载进度。我目前正在使用具有单元格实例并更新进度条的解析进度 block 。
}, progressBlock: { (percent) in
self.mainQueue.addOperation {
// set the downloadProgess var to update from cellForItemAt
// downloadProgress = (Float(percent) / Float(100))
if let downloadingCell = self.collectionView.cellForItem(at: self.indexPath) as? InnerCollectionCell {
downloadingCell.progressBar.isHidden = false
downloadingCell.contentView.bringSubview(toFront: downloadingCell.progressBar)
downloadingCell.progressBar.setProgress(Float(percent) / Float(100), animated: true)
downloadingCell.setNeedsDisplay()
downloadingCell.setNeedsLayout()
downloadingCell.isUserInteractionEnabled = false
downloadingCell.spinner.isHidden = true
}
}
})
所以这工作正常,我现在遇到的问题是,如果我离开这个 View Controller ,然后回来查看下载情况,单元格的实例已被重用,并且没有任何所需的 UI 元素可见,但进度仍在滴答作响。
我认为唯一可以重新显示 UI 元素的地方是 cellForItemAt。那么问题是进度不会更新,它只显示重新加载单元格时的值。
我怎样才能重新使用进度 block 正在使用的单元格实例或干净地显示继续更新的 ui 元素?
最佳答案
假设您要用 Collection View 关闭旧 View Controller 并呈现一个新 View Controller ,这里有两个问题:
然后您将尝试更新前一个 View Controller 中 Collection View 中的单元格;和
您正在保留对已关闭的旧 View Controller 的强引用。
如果是这种情况,目标是将进度更新与任何特定的 View Controller 、 Collection View 或单元分离。您可能还想分离项目/行号,以防您随时插入/删除任何单元格。处理此问题的最佳方法是通知:
定义一些在定义通知时使用的常量:
private let notificationName = Notification.Name(rawValue: "com.domain.app.downloadProgress") private let notificationIdentifierKey = "com.domain.app.download.identifier" private let notificationPercentKey = "com.domain.app.download.percent"
让您的
progressBlock
发布通知,而不是尝试直接更新 UI:let percent: Float = ... let userInfo: [AnyHashable: Any] = [ notificationIdentifierKey: identifier, notificationPercentKey: percent ] NotificationCenter.default.post(name: notificationName, object: nil, userInfo: userInfo)
请注意,这里没有对
self
的引用,这可以防止进度 block 卡在您的 View Controller 上。定义一些函数,您可以使用这些函数来识别哪个
IndexPath
对应于您的下载标识符。在我的简单示例中,我只需要一个下载标识符数组并使用它:var downloadIdentifiers = [String]() private func indexPath(for identifier: String) -> IndexPath? { if let item = downloadIdentifiers.index(of: identifier) { return IndexPath(item: item, section: 0) } else { return nil } }
您可能有一个下载标识符作为某些
Download
模型对象的属性,并使用它来代替,但希望它能说明这个想法:只要有一些方法来识别适当的给定下载的 IndexPath
。 (顺便说一句,将IndexPath
与您首次创建下载时的内容分离很重要,以防您随时从 Collection View 中插入/删除任何项目。)现在,您可能会问应该使用什么作为标识符。您可以使用 URL 的
absoluteString
。您可能会使用其他一些唯一标识符。但我不鼓励您仅依赖项目/行号,因为这些可能会改变(也许现在不会,但以后当您使应用程序变得更加复杂时,您可能会插入和删除项目)。让您的 Collection View 的 View Controller 将自己添加为该通知的观察者,更新适当的进度 View :
private var observer: NSObjectProtocol! override func viewDidLoad() { super.viewDidLoad() observer = NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: .main) { [weak self] notification in if let identifier = notification.userInfo?[notificationIdentifierKey] as? String, let percent = notification.userInfo?[notificationPercentKey] as? Float, let indexPath = self?.indexPath(for: identifier), let cell = self?.collectionView?.cellForItem(at: indexPath) as? InnerCollectionCell { cell.progressView.setProgress(percent, animated: true) } } ... } deinit { NotificationCenter.default.removeObserver(observer) }
请注意
[weak self]
捕获列表,以确保通知观察者不会导致与 View Controller 的强引用循环。
关于ios - 在可重复使用的单元格中显示下载进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42181912/