UICollectionView单元重用和在线图像

时间:2018-09-21 20:28:44

标签: swift uikit

我试图了解UICollectionView单元重用的工作原理。

我目前正在实现具有大单元格的水平滚动UICollectionView,该单元格几乎占据了屏幕的整个大小。大约有100多个单元格,但一次只能看到3个。

据我了解,UICollectionView单元重用只是维护了一个初始化的单元对象池,这样当一个单元格不在视野中时,它可以被新可见的单元格蚕食。也就是说,由于我正在使用重用,因此该集合可能只会在内存中初始化〜3个实际的单元对象,而我只会切换它们的内容。

对于自定义单元格具有基于需要下载的图像的图像视图的情况,我非常担心这意味着什么。理想情况下,我会有一个场景,每个单元格图像仅下载一次,并且仅在绝对必要时才下载。

如果确实存在我的自定义单元对象池,那么这意味着完全没有发生。每当看到一个单元格时,我便开始进行全新下载。

我应该怎么做呢?

我问这个问题的主要原因是,在滚动时(特别是在初始滚动时),我确实看到旧单元格的图像与该单元格应该显示的图像之间有些闪烁。我已修复,但我可以肯定的是,它导致在线图像被下载了太多次。我这样做正确吗?

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath) as? ImageCell else {
        assert(false)
    }
    let image = data[indexPath.row]
    cell.display(title: image.title, imageURL: image.imageURL)
    return cell
}

还有单元格

public class NewsCell: UICollectionViewCell {
    private var title: UILabel = UILabel()
    private var imageView: UIImageView = UIImageView()

    override public init(frame: CGRect = CGRect.zero) {
        super.init(frame: frame)

        title.font = UIFont.systemFont(ofSize: 12, weight: .bold)
        title.textColor = UIColor.white
        title.textAlignment = NSTextAlignment.left

        imageView.contentMode = UIViewContentMode.scaleAspectFill
        imageView.clipsToBounds = true

        contentView.addSubview(imageView)
        imageView.addSubview(title)

    // Layout constraints
    }

    public func display(title: String, imageURL: URL?) {
        self.imageView.image = nil
        self.title.text = title
        if let url = imageURL {
            downloadImage(from: url)
        }
    }

    func downloadImage(from url: URL) {
        getData(from: url) { data, response, error in
            guard let data = data, error == nil else {
                return
            }
            DispatchQueue.main.async {
                self.imageView.image = UIImage(data: data)
            }
        }
    }

    func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
        URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
    }

}

我担心必须将显示的图像视图图像设置为零,以防止闪烁。我应该采取其他措施来避免轻率地下载这些图像,还是看起来不错?

1 个答案:

答案 0 :(得分:0)

  1. 您可以使用Prefetching Collection View Data来更早加载图像。
  

当加载数据是一个缓慢或昂贵的过程时(例如,通过网络获取数据时),请使用数据预取。在这种情况下,请异步执行数据加载。

这当然需要进行一些更改。您将必须分别存储图像并在调用DataSourcePrefetching方法时下载它们。另外,然后下载图像,您需要检查是否有任何等待该图像的单元。因此,您的UICollectionView将不再下载任何内容。它将仅显示图像,或等待下载。

  1. 您当前的解决方案有问题。如果滚动速度太快,则可能由于重复使用而出现问题,因为同一UICollectionViewCell会一次加载几张图像。在这种情况下,用户将只看到最后一个,而您永远也看不出它是哪个。为避免这种竞争情况,您可以将图像标识符或其URL存储在单元格中,以便在下载完成后,它可以检查下载的图像是否正确。

  2. 在下载时将image设置为nil是可以的,但是您也可以向用户显示一些UIActivityIndicatorView,以便他可以看到正在发生的事情。

    < / li>