在表视图中异步加载图像
Loading images async in tableview
我写了一个异步方法来将图片加载到表视图中。但问题是图像没有加载,因为它不会在滚动后自动更新。我尝试添加:
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
但这只会一直用不同的图像刷新行。第二个问题是滚动后图像会显示,但有时图像会加载到错误的行中。
有人能帮我解决这个问题吗,因为我想不通。
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ArticleCell", for: indexPath)
let article = newsArticles!.articles[indexPath.row]
cell.textLabel?.text = article.title
cell.detailTextLabel?.text = article.content
cell.imageView?.image = nil
if let url = article.urlToImage {
imageLoader.obtainImageWithPath(imagePath: url) { (image) in
if let updateCell = tableView.cellForRow(at: indexPath) {
updateCell.imageView?.image = image
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
}
}
}
return cell
}
图像加载器:
class ImageLoader {
var imageCache = NSCache<NSString, UIImage>()
init() {
self.imageCache = NSCache()
}
func obtainImageWithPath(imagePath: URL, completionHandler: @escaping (UIImage) -> ()) {
if let image = self.imageCache.object(forKey: imagePath.absoluteString as NSString) {
DispatchQueue.main.async {
completionHandler(image)
}
} else {
let placeholder: UIImage = UIImage(named: "placeholderImage")!
DispatchQueue.main.async {
completionHandler(placeholder)
}
let task = URLSession.shared.dataTask(with: imagePath, completionHandler: {data, response, error in
let image: UIImage! = UIImage(data: data!)
self.imageCache.setObject(image, forKey: imagePath.absoluteString as NSString)
DispatchQueue.main.async {
completionHandler(image)
}
})
task.resume()
}
}
}
更新您的 ImageLoader
代码,让您检查图像是否已在缓存中并 return 同步给您。
然后你加载cell的时候,如果有图片,马上设置。如果它没有图像,则让完成处理程序在该索引路径处的单元格上重新加载。然后,由于图像现在已缓存,您的常规单元格加载代码将能够填充图像。只要确保您仅在尚未从缓存中设置图像时才请求该图像。
你现在的代码编写方式,无论它是否在你的完成处理程序中从缓存中设置图像,它都会无休止地尝试重新加载它刚刚设置图像的行,这也会产生影响表现。因此,正如我所说,为什么只有在刚刚下载了新图像时才应该重新加载单元格。并且不要在完成处理程序中设置图像,只需重新加载该行。
我写了一个异步方法来将图片加载到表视图中。但问题是图像没有加载,因为它不会在滚动后自动更新。我尝试添加:
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
但这只会一直用不同的图像刷新行。第二个问题是滚动后图像会显示,但有时图像会加载到错误的行中。
有人能帮我解决这个问题吗,因为我想不通。
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ArticleCell", for: indexPath)
let article = newsArticles!.articles[indexPath.row]
cell.textLabel?.text = article.title
cell.detailTextLabel?.text = article.content
cell.imageView?.image = nil
if let url = article.urlToImage {
imageLoader.obtainImageWithPath(imagePath: url) { (image) in
if let updateCell = tableView.cellForRow(at: indexPath) {
updateCell.imageView?.image = image
tableView.reloadRows(at: [indexPath], with: UITableView.RowAnimation.none)
}
}
}
return cell
}
图像加载器:
class ImageLoader {
var imageCache = NSCache<NSString, UIImage>()
init() {
self.imageCache = NSCache()
}
func obtainImageWithPath(imagePath: URL, completionHandler: @escaping (UIImage) -> ()) {
if let image = self.imageCache.object(forKey: imagePath.absoluteString as NSString) {
DispatchQueue.main.async {
completionHandler(image)
}
} else {
let placeholder: UIImage = UIImage(named: "placeholderImage")!
DispatchQueue.main.async {
completionHandler(placeholder)
}
let task = URLSession.shared.dataTask(with: imagePath, completionHandler: {data, response, error in
let image: UIImage! = UIImage(data: data!)
self.imageCache.setObject(image, forKey: imagePath.absoluteString as NSString)
DispatchQueue.main.async {
completionHandler(image)
}
})
task.resume()
}
}
}
更新您的 ImageLoader
代码,让您检查图像是否已在缓存中并 return 同步给您。
然后你加载cell的时候,如果有图片,马上设置。如果它没有图像,则让完成处理程序在该索引路径处的单元格上重新加载。然后,由于图像现在已缓存,您的常规单元格加载代码将能够填充图像。只要确保您仅在尚未从缓存中设置图像时才请求该图像。
你现在的代码编写方式,无论它是否在你的完成处理程序中从缓存中设置图像,它都会无休止地尝试重新加载它刚刚设置图像的行,这也会产生影响表现。因此,正如我所说,为什么只有在刚刚下载了新图像时才应该重新加载单元格。并且不要在完成处理程序中设置图像,只需重新加载该行。