我有一个UITableView
,我需要每个单元格从提供的URL下载图像并显示它。这看起来是一个非常常见的场景,事实上我发现了几个与此相关的帖子和问题,但我还不清楚应该采用哪种最佳方法。
首先,我有一个使用Data(contentsOf:)
。这是我UITableViewCell
中的代码:
class MyCell: UITableViewCell {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var imageView: UIImageView!
var model: Model? {
willSet {
activityIndicator.startAnimating()
configureImage(showImage: false, showActivity: true)
}
didSet {
guard let imageSmallUrlStr = model?.imageSmallUrl, let imageUrl = URL(string: imageSmallUrlStr) else {
activityIndicator.stopAnimating()
configureImage(showImage: false, showActivity: false)
return
}
DispatchQueue.global(qos: .userInitiated).async {
var image: UIImage?
let imageData = try? Data(contentsOf: imageUrl)
if let imageData = imageData {
image = UIImage(data: imageData)
}
DispatchQueue.main.async {
self.imageView.image = image
self.configureImage(showImage: true, showActivity: false)
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
configureImage(showImage: false, showActivity: false)
}
override func prepareForReuse() {
super.prepareForReuse()
model = nil
}
// Other methods
}
这种方法看起来非常简单快速,至少在我测试它在模拟器上运行时。虽然,我有一些问题:
HTTP
从Data(contentsOf:)
网址下载图片是否合适?它可以工作,但它可能更适合获取你拥有的文件或其他类型的东西,而且它不是网络的最佳解决方案。现在,这是我的另一种方法,使用URLSession
:
class MyCell: UITableViewCell {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBOutlet weak var imageView: UIImageView!
var imageTask: URLSessionDownloadTask?
var model: Model? {
willSet {
activityIndicator.startAnimating()
configureImage(showImage: false, showActivity: true)
}
didSet {
imageTask?.cancel()
guard let imageSmallUrlStr = model?.imageSmallUrl, let imageUrl = URL(string: imageSmallUrlStr) else {
activityIndicator.stopAnimating()
configureImage(showImage: false, showActivity: false)
return
}
imageTask = NetworkManager.sharedInstance.getImageInBackground(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(showImage: true, showActivity: false)
}
})
}
}
// Other methods
}
通过这种方法,我可以手动取消任务,但是当我在模拟器上运行应用程序时,它看起来更慢。事实上,我正在取消一些"取消"尝试下载许多图像时出错。但这样我就可以处理后台下载。
关于这种方法的问题:
这个问题与两种方法有关:
答案 0 :(得分:0)
您绝不应该使用NSData的contentsOfURL方法来检索非本地数据,原因有两个:
在你的情况下,在并发队列上执行它,最后一个并不像它本来那样糟糕,但是如果你获取足够的资源,我怀疑它会导致相当大的开销。
您的NSURLSession方法显得较慢的一个原因是我认为您可能正在使用其他方法同时获取所有资源(非常难以点击服务器)。更改NSURLSession中的并发限制可能会稍微改善其性能,但只有在您知道服务器可以处理它时才会这样做。
回答其他问题:
您可以通过多种方式将图像绑定到特定单元格,但最常见的方法是拥有一个管理所有请求的单例,并在该单例中:
在任何一种情况下,都要将URL存储在单元格的属性中,并确保它在设置图像之前与请求的原始URL匹配,这样如果单元格被重用,则不会用错误覆盖其现有图像来自陈旧请求的图片。