Ios 在适当的单元格中下载并显示图像

Ios 在适当的单元格中下载并显示图像,ios,image,uitableview,nsurlsession,nsurlsessiondownloadtask,Ios,Image,Uitableview,Nsurlsession,Nsurlsessiondownloadtask,我通过调用异步网络任务,为每个需要在UITableView中显示的单元格下载一个图像。这在表的UIViewController类中: override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard results.count > 0 else { return UITableViewCell()

我通过调用异步网络任务,为每个需要在
UITableView
中显示的单元格下载一个图像。这在表的
UIViewController
类中:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard results.count > 0 else {
        return UITableViewCell()
    }

    let myCell = tableView.dequeueReusableCell(withIdentifier: CustomCell.cellIdentifier, for: indexPath) as! CustomCell
    let model = results[indexPath.row]
    myCell.model = model
    return myCell
}
这是
CustomCell

class CustomCell: UITableViewCell {

// Several IBOutlets

static let cellIdentifier = "myCell"
let imageProvider = ImageProvider()

var model: MyModel? {
willSet {
    activityIndicator.startAnimating()
    configureImage(showImage: false, showActivity: true)
}
didSet {
    guard let modelUrlStr = model?.imageUrlStr, let imageUrl = URL(string: modelUrlStr) else {
        activityIndicator.stopAnimating()
        configureImage(showImage: false, showActivity: false)
        return
    }

    imageProvider.getImage(imageUrl: imageUrl, completion: {[weak self] (image, error) in
        DispatchQueue.main.async {
            guard error == nil else {
                self?.activityIndicator.stopAnimating()
                self?.configureImage(showImage: false,  showActivity: false)
                return
            }

            self?.imageView.image = image
            self?.activityIndicator.stopAnimating()
            self?.configureImage(showCoverImage: true, showActivity: false)
        }
    })
}
}

override func awakeFromNib() {
super.awakeFromNib()
configureImage(showCoverImage: false, showActivity: false)
}

override func prepareForReuse() {
super.prepareForReuse()
model = nil
}

private func configureImage(showImage: Bool, showActivity: Bool) {
// Update image view
} 
}
ImageProvider

class ImageProvider {
var imageTask: URLSessionDownloadTask?

func getImage(imageUrl: URL, completion: @escaping DownloadResult) {
imageTask?.cancel()

imageTask = NetworkManager.sharedInstance.getImageInBackground(imageUrl: imageUrl, completion: { (image, error) -> Void in
    if let error = error {
        completion(nil, error)
    } else if let image = image {
        completion(image, nil)
    } else {
        completion(nil, nil)
    }
})
}
}
由于单元格可以动态地排队和重用,并且下载是异步的,然后可以在滚动时在每个单元格上重用一个图像,我这样做是否可以确保每个单元格始终显示其相应的图像

编辑:不同的方法

在单元格中保留对模型的引用是否合适?考虑一个正确的
MVC
体系结构。谁应该负责下载图片?单元格(仅向其传递URL而不是完整的模型对象)或表视图的视图控制器(在其
tableView:cellForRowAt:
方法中更新单元格中的图像)

  • ImageProvider
    添加一个方法,该方法允许您取消基础
    URLSessionDownloadTask
    。当单元格即将被重用时调用此方法。为此,在cell子类中重写
    prepareforuse()
  • 在发生重用时,任务可能已经完成,但
    DispatchQueue.main.async
    块仍在排队,并将在单元重用发生后激发。为了缓解这种情况,您需要对照存储在模型中的URL检查完成任务的URL
    谢谢我在想。。。实际上,将模型放在单元格中是否正确?这打破了MVC的模式,对吗?我见过一些这样做的例子,将模型传递到cell并在那里处理网络下载,但将这种逻辑移动到表视图的视图控制器是否更合适?@AppsDev我建议您做一些让您感觉不那么尴尬的事情。若严格遵守体系结构模式会让你们为了一个不明确的好处而增加复杂性,我会说避免它。在这种情况下,像您的示例中那样完成工作可能是完全正确的。这取决于系统的结构。跟随你的直觉:)