从 TableView 中的 JSON 数据下载可重复使用的单元格或错误的异步图像

Reusable cell or wrong asynchronous image download from JSON data in TableView

我有一个从 API 下载 JSON 数据并将其设置在表格视图单元格中的应用程序。在某些时候,通过在随机单元格图像中滚动 up/down 可能会在它有正确的图像之前改变自己几次。文本数据没有任何问题。起初我以为问题是单元格的可重用性,所以我需要在实际设置新图像之前销毁数据。所以我将 TableViewCell 的这种方法添加到 cellForRowAt 路径以在设置单元格之前销毁数据:

func destroyCellData() {

    self.newsImageView.image = nil
    self.newsTitleLabel.text = nil
    self.tagLabel.text = nil
}

那么实际设置单元格的方法:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    guard let cell = tableView.dequeueReusableCell(withIdentifier: "newscell") as? NewsTableViewCell else {

        print("No such cell")
        exit(14)
    }

    cell.loadingImageIndicator.startAnimating()

    let data = dataManager.getData()

    let dataToInsert = data[indexPath.row]
    cell.destroyCellData()
    cell.setDataToCell(with: dataToInsert)

    return cell
}

方法 setDataToCell 是:

func setDataToCell(newsObject: jsonNews) {

        self.newsTitleLabel.text = newsObject.title
        self.tagLabel.text = newsObject.tag

        let url = newsObject.imageURL
        self.newsImageView.loadImageUsingCacheWithURLString(url, placeholder: UIImage(named: "ViewPlaceholder"), indicator: loadingImageIndicator)

}

最后我在单元格中异步加载图像:

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {

func loadImageUsingCacheWithURLString(_ URLString: String, placeholder: UIImage?, indicator: UIActivityIndicatorView? = nil) {

    self.image = nil

    if let cachedImage = imageCache.object(forKey: NSString(string: URLString)) {

        self.image = cachedImage
        indicator?.stopAnimating()
        return

    }

    guard let encodedString = URLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {

        print("Error Loading URL")
        return
    }

    if let url = URL(string: encodedString) {

        URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in

            if error != nil {

                print("ERROR LOADING IMAGES FROM URL: \(String(describing: error))")

                OperationManager.UI {

                    self.image = placeholder
                    indicator?.stopAnimating()
                }

                return
            }

                if let data = data {

                    if let downloadedImage = UIImage(data: data) {

                            print(URLString)
                            imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))

                        OperationManager.UI {

                            self.image = downloadedImage
                            indicator?.stopAnimating()
                    }
                }
            }
        }).resume()
    }
}

}

所以我的问题是 - 问题出在我下载和设置图像的方法上,还是出在单元格的可重用性上?如果可能的话,给我一个更合适的方法来将图像设置为单元格的提示。提前致谢

您可以使用 https://github.com/onevcat/Kingfisher 进行异步图像加载。这负责为相应的索引

缓存和渲染正确的图像

没有你的代码,我怀疑你的问题可能与可重用单元格和异步图像下载有关。这样,当您重新加载单元格时,它会启动一个数据任务来下载图像,如果第二个任务开始下载不同的图像,则第一个不会发生任何事情。

例如,假设您的第一张图片为 100MB,可重复使用单元格的第二张图片为 1KB。希望 1KB 下载速度更快。这是流程

  1. 称重传感器 image1
  2. 开始下载 image1
  3. 称重传感器 image2
  4. 开始下载 image2
  5. 完成 image2
  6. 下载
  7. 将细胞图像设置为image2
  8. 完成 image1
  9. 下载
  10. 将细胞图像设置为image1

由于image1的数据下载任务是在image2已经设置完成后,cell会在显示image2后更新为显示image1,从而显示错误的图片。

要解决您的问题,您需要以某种方式取消第一个操作,或者更确切地说,如果不再显示该数据,则不更新图像视图。有很多方法可以做到这一点。一种解决方案是在您的单元格上创建图像 url 的 属性,下载完成后,确认其正确 url,如果正确,则更新图像视图。

func setDataToCell(newsObject: jsonNews) {
    // Your other code
    self.imageURL = newsObject.imageURL
}

// loadImageUsingCacheWithURLString
if let downloadedImage = UIImage(data: data) {

    print(URLString)
    imageCache.setObject(downloadedImage, forKey: NSString(string: URLString))

    OperationManager.UI {
        if url.absoluteString == self.imageURL {
            self.image = downloadedImage
            indicator?.stopAnimating()
        } 
    }
}

注意:没有测试任何这段代码,而是直接从我的脑海中写出来的。它可能有错误。

综上所述,我建议使用第三方图像缓存系统,例如 AlamofireImage 或人们评论过的其他系统之一。