显示可重用单元格中的下载进度

Pip*_*ppo 1 uiprogressview ios parse-platform uicollectionview swift

我试图在我的collectionview单元格中显示下载进度.我当前正在使用具有单元实例的解析进度块并更新进度条.

}, 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
    }
}
})
Run Code Online (Sandbox Code Playgroud)

所以这个工作正常,我现在遇到的问题是,如果我离开这个视图控制器,然后回来看看下载是如何进行的,单元格的实例已被重用,并且没有任何所需的UI元素可见,但进度仍在滴答作响在背景中.

我可以想到重新显示UI元素的唯一地方是在cellForItemAt中.那么问题是进度不会更新,它只显示重新加载单元格时的值.

我如何重用进度块正在使用的单元格实例或干净地显示继续更新的ui元素?

Rob*_*Rob 10

假设您使用集合视图解除旧视图控制器并呈现新视图控制器,则此处存在两个问题:

  • 然后,您尝试更新上一个视图控制器中集合视图中的单元格; 和

  • 你对被解雇的旧视图控制器保持强烈的引用.

如果是这种情况,目标是从任何特定视图控制器,集合视图或单元格中分离进度更新.您也可能希望解除项目/行号,以防您随时插入/删除任何单元格.处理此问题的最佳方法是通知:

  1. 定义通知时使用的一些常量:

    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"
    
    Run Code Online (Sandbox Code Playgroud)
  2. 让您的progressBlock帖子发布通知,而不是尝试直接更新UI:

    let percent: Float = ...
    let userInfo: [AnyHashable: Any] = [
        notificationIdentifierKey: identifier,
        notificationPercentKey: percent
    ]
    NotificationCenter.default.post(name: notificationName, object: nil, userInfo: userInfo)
    
    Run Code Online (Sandbox Code Playgroud)

    请注意,此处没有引用self,这会阻止进度块挂在视图控制器上.

  3. 定义一些可用于识别哪个功能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
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    你可能有一个下载标识符作为某个Download模型对象的属性,并使用它,但希望它说明了这个想法:只是有一些方法来确定适合IndexPath给定的下载.(顺便说一句,这与IndexPath您第一次创建下载时的内容非常重要,以防您在任何时候从集合视图中插入/删除任何项目.)

    现在,您可以询问您应该使用什么标识符.您可以使用URL absoluteString.您可以使用其他一些唯一标识符.但是我不鼓励你完全依赖于项目/行号,因为那些可以改变(可能不是现在,但也许稍后你使应用程序更复杂,你可能会插入删除项目).

  4. 让您的集合视图的视图控制器将其自身添加为此通知的观察者,更新相应的进度视图:

    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)
    }
    
    Run Code Online (Sandbox Code Playgroud)

    请注意[weak self]捕获列表,以确保通知观察器不会导致视图控制器出现强引用周期.