【问题标题】:Kingfisher image render recycling cells翠鸟图像渲染回收单元
【发布时间】:2018-04-21 23:23:37
【问题描述】:

我正在使用 Kingfisher 下载和缓存我的图像。我正在使用 CustomImageView 下扩展 ImageView 的自定义类。我还使用自定义 UITableViewCell 在 CustomCell 类文件的 didSet 属性中调用 loadImage 方法。

下面的方法 loadImage 是所有魔法发生的地方。

class CustomImageView : UIImageView {
var lastUrlLoaded: String?

func loadImage(url:String) {
    lastUrlLoaded = url
    ImageCache.default.retrieveImage(forKey: url, options: nil) { (image, cacheType) in
        if let image = image {
            self.image = image
            return
        }
        else {
            guard let url = URL(string: url) else { return }
            self.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil) {
                (image, error, cacheTye, _) in
                if let err = error {
                    self.kf.indicatorType = .none
                    DispatchQueue.main.async {
                      self.image = #imageLiteral(resourceName: "no_image_available") 
                    }
                    print ("Error loading Image:", err)
                    return
                }

                if url.absoluteString != self.lastUrlLoaded { return }
                if let image = image {
                    ImageCache.default.store(image, forKey: url.absoluteString)
                    DispatchQueue.main.async {
                        self.image = image
                    }
                }
            }
        }
    }
}

// Custom TableViewCell -- NewsCell.file
 class NewsCell: UITableViewCell {
var article: Article? {
    didSet{
        guard let image_url = article?.image_url else { return }
        _image.kf.indicatorType = .activity
        _image.loadImage(url: image_url, type: "news")
      }
   }
}

 // METHOD in my NewsController
 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: 
  IndexPath) -> UITableViewCell {        
   let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! NewsCell
      cell.article = articles[indexPath.item]
return cell
}

我在快速滚动单元格时遇到问题,一些已加载的图像被用于错误的单元格。我已经研究并阅读了其他帖子,但没有答案对我有帮助。我了解它正在处理后台任务,但不确定如何解决它。如果有人可以帮助或指出我正确的方向,将不胜感激。

我在 stackoverflow 上看到并尝试过的解决方案:

  1. prepareForReuse 在自定义单元格中将图像设置为 nil。
  2. 在 cellforrowatindexpath 处将图像设置为零。

【问题讨论】:

  • 您能否澄清一下:“当我快速滚动时.. 已加载的图像被用于错误的单元格”。所以,如果你不滚动,只是等待一切加载,一切看起来都很完美吗?如果你慢慢滚动,一切看起来都很完美?仅当您在发生异步图像加载时快速滚动时,图像才会出现错位。是这样吗?你的行高度是恒定的吗?所有图像的高度都相同吗?如果图像替换导致行高发生变化,您可能需要使用开始/结束更新。
  • 因此,如果我缓慢滚动或不滚动视图,直到第一组单元格加载它们的图像,一切都会按预期加载——当我说快速滚动时,我指的是向上滚动和向下通过视图将在后台加载异步图像。我所有的图像都是相同的高度,不会影响单元格的高度。
  • 好的,显示你的 cellforrowatindexpath 和 didset 来自你的 cell 类。
  • @ozzieozumo 我更新了 sn-p 以显示您询问的方法
  • 好的,看起来你走在了正确的轨道上。现在,您说您已尝试“在 cellforrowatindexpath 处将图像设置为零”,但没有成功。它应该工作。确保使用此顺序:a)出队 b)无图像 c)设置文章 d)返回单元格。也许使用您尝试使用此技术的代码更新您的问题。

标签: ios swift uitableview uicollectionviewcell kingfisher


【解决方案1】:

问题

当您快速滚动时,您正在重复使用单元格。重复使用的单元格包含 KF 图像,这些图像可能具有正在运行的下载任务(从对 kf.setImage 的调用开始),用于回收文章中的图像。如果允许该任务完成,它将使用旧图像更新您的新单元格。我会建议两种方法来解决它。

答案 1 - 杀死回收的下载任务

请参阅 Kingfisher 备忘单Canceling a downloading task

备忘单显示了如何在表格视图的 didEndDisplaying 中停止这些任务。我在这里引用它:

// In table view delegate
func tableView(_ tableView: UITableView, didEndDisplaying cell: 
UITableViewCell, forRowAt indexPath: IndexPath) {
    //...
    cell.imageView.kf.cancelDownloadTask()
}

您可以在那里或在您的单元类的 didSet 观察器中执行此操作。你明白了。

答案 2 - 不要杀死他们,只是忽略结果

另一种方法是允许下载任务完成,但仅在下载的 URL 与当前所需的 URL 匹配时更新单元格图像。也许你正试图用你的 lastUrlLoaded 实例变量做类似的事情。

你在 kf.setImage 的完成处理程序中有这个:

if url.absoluteString != self.lastUrlLoaded { return }

这就是我的意思,只是您没有正确设置该变量以使其工作。为了解释起见,我们假设它被称为“desiredURL”。然后你可以在 didSet 观察者中设置它:

_image.desiredURL = image_url

并在kf.setImage的completion handler中测试:

// phew, finished downloading, is this still the image that I want?
if url != self.desiredURL { return }

注意事项

如果是我,我肯定会采用第二种方法(它具有缓存下载图像的积极副作用,但我也不相信取消任务)。

此外,您似乎正在尝试做 kf.setImage 为您做的艰难的事情,即加载缓存等。我相信您有您的理由,无论哪种方式您仍然需要处理这个问题。

【讨论】:

  • 感谢您的帮助 — 我试了一下,但仍然是同样的问题 :( — 不知道为什么,因为理论上它应该可以工作。每个开发人员都知道编码不像第一个那样工作时间大声笑
  • 如果您发布更新,我很乐意查看您的代码。有点意思。
  • @ozzieozumo 感谢您的回答。我使用了您的第一种方法,取消了以前的下载。我唯一不同的是在cellForRow 中调用cell.imageView.kf.cancelDownloadTask() 而不是didEndDisplaying
  • 当我尝试使用 cancelDownloadTask 时,控制台会收到类似以下内容的日志:Task <08624AEE-9A76-47EE-AFBC-8836CCDD5213>.<10> load failed with error Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://image.tmdb.org/t/p/w45/2vrnA7cg6D7EmOlwivGsuHnk2f9.jpg, NSErrorFailingURLKey=https://image.tmdb.org/t/p/w45/2vrnA7cg6D7EmOlwivGsuHnk2f9.jpg, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <08624AEE-9A76-47EE-AFBC-8836CCDD5213>.<10>". 我不确定如何使用 cancelDownloadTask 而不打印此错误。
【解决方案2】:
override func prepareForReuse() {
    super.prepareForReuse()
    imageView.kf.cancelDownloadTask() // first, cancel currenct download task
    imageView.kf.setImage(with: URL(string: "")) // second, prevent kingfisher from setting previous image
    imageView.image = nil
}

【讨论】:

    【解决方案3】:

    在您的模型中检查 url 是否不存在将其设置为 nil,然后在单元格中检查您的 url 是否存在设置它的图像,否则为其设置一个占位符。

    if article?.image_url != nil{
      _image.kf.indicatorType = .activity
      _image.loadImage(url: image_url, type: "news")
    }else{
      _image = UIImage(named: "placeholder")
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-26
      相关资源
      最近更新 更多