在快速滚动时,在UITableView单元格中异步加载错误的图片

Pas*_*cal 1 asynchronous image reusability ios swift

我有一个UITable-View,其中包含用户列表及其个人资料图片.我正在为每个玩家异步加载图片(http:// api/pictures / {userid}):

func loadImageAsync(imageUrl: URL, completionHandler handler: @escaping (_ success: Bool, _ image: UIImage?) -> Void){

DispatchQueue.global(qos: .userInitiated).async { () -> Void in

    if let imgData = try? Data(contentsOf: imageUrl), let img = UIImage(data: imgData) {
        DispatchQueue.main.async(execute: { () -> Void in
            handler(true, img)
        })
    } else {
        handler(false, nil)
    }
}
Run Code Online (Sandbox Code Playgroud)

在cellForRowAt-Index-Fuction的完成处理程序中,我正在设置图片.

       loadImageAsync(imageUrl: imageUrl!, label: ip) { (success, image, backlabel) -> Void in
            if(success){
                cell.profilePictureView.image = image
            }
        }
Run Code Online (Sandbox Code Playgroud)

但是,当我滚动得非常快时,一些图片会被加载到错误的单元格中.为了防止重用问题,我在每次重用后"重置"图像视图:

    override func prepareForReuse() {
        profilePictureView.image  = UIImage(named: "defaultProfilePicture")
    }
Run Code Online (Sandbox Code Playgroud)

但是为什么在快速滚动时仍然会有一些图像加载错误?

嗯,这也是我的想法.

__Update:因此,我使用Label参数(类型为Any)扩展了该函数,该函数在放入函数时返回.我试图将参数(使用索引路径)与当前索引路径进行比较.实际上,这应该工作 - 不应该吗?!

            loadImageAsync(imageUrl: imageUrl!, label: ip) { (success, image, backlabel) -> Void in

            cell.loader.stopAnimating()

            if (backlabel as! IndexPath == indexPath) {

                //set image...
Run Code Online (Sandbox Code Playgroud)

但是,它没有显示任何效果.你知道为什么或有任何其他解决方案来解决这个问题吗?

Dun*_*n C 6

问题是,如果您快速滚动,下载可能需要足够长的时间,在完成时,相关单元格已从屏幕滚动并被回收用于数据模型中的不同indexPath.

诀窍是在完成块中询问表视图中的indexPath处的单元格,并且只有在返回单元格时才安装图像:

   loadImageAsync(imageUrl: imageUrl!, label: ip, for indexPath: IndexPath) { (success, image, backlabel) -> Void in
        if(success){
            let targetCell = tableview.cell(for: indexPath)
            targetCell.profilePictureView.image = image
        }
    }
Run Code Online (Sandbox Code Playgroud)

编辑:

像这样重新定义loadImageAsync函数:

func loadImageAsync(imageUrl: URL,
  indexPath: IndexPath, 
  completionHandler handler: @escaping (_ success: Bool, 
    _ image: UIImage?,
    _ indexPath: IndexPath ) -> Void) { ... }
Run Code Online (Sandbox Code Playgroud)

编辑#2

顺便说一句,您应该将图像保存到磁盘并从那里加载它们,而不是每次都从Internet加载.我建议使用图像URL的哈希值作为文件名.

修改loadImageAsync,如下所示:

  1. 检查磁盘上是否已存在该文件.如果是这样,请加载并返回.

  2. 如果该文件不存在,请执行异步加载,然后使用URL的哈希值作为文件名将其保存到磁盘,然后再返回内存中的映像.