在适当的单元格中下载和显示图像
Downloading and showing images in the appropriate cell
我通过调用异步网络任务为每个需要在 UITableView
中显示的单元格下载图像。这是在UIViewController
class的table:
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 而不是完整的模型对象),或 table 视图的视图控制器(在其 tableView:cellForRowAt:
方法中更新单元格中的图像)?
- 向您的
ImageProvider
添加一个允许您取消基础 URLSessionDownloadTask
的方法。当单元格即将被重用时调用此方法。为此,在您的单元格子类中覆盖 prepareForReuse()
。
- 有可能任务在重用发生时已经完成,但
DispatchQueue.main.async
块仍在排队,将在单元格重用发生后触发。为了缓解这种情况,您需要检查已完成任务的 URL 是否与存储在模型中的 URL 相对应。
我通过调用异步网络任务为每个需要在 UITableView
中显示的单元格下载图像。这是在UIViewController
class的table:
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 而不是完整的模型对象),或 table 视图的视图控制器(在其 tableView:cellForRowAt:
方法中更新单元格中的图像)?
- 向您的
ImageProvider
添加一个允许您取消基础URLSessionDownloadTask
的方法。当单元格即将被重用时调用此方法。为此,在您的单元格子类中覆盖prepareForReuse()
。 - 有可能任务在重用发生时已经完成,但
DispatchQueue.main.async
块仍在排队,将在单元格重用发生后触发。为了缓解这种情况,您需要检查已完成任务的 URL 是否与存储在模型中的 URL 相对应。