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

时间:2017-05-07 18:03:44

标签: ios image uitableview nsurlsession nsurlsessiondownloadtask

我通过调用异步网络任务,每个需要在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:方法更新单元格中的图像)?

1 个答案:

答案 0 :(得分:0)

  1. ImageProvider添加一个允许您取消基础URLSessionDownloadTask的方法。当要重复使用该单元时调用此方法。为此,请覆盖单元子类中的prepareForReuse()
  2. 在重用发生的时候,任务有可能已经完成,但DispatchQueue.main.async块仍然排队,并且在发生单元重用后将被触发。要缓解这种情况,您需要根据模型中存储的URL检查已完成任务的URL。