将异步 Gif 加载到滚动的 UICollectionView

Loading Aync Gifs to Scrolling CollectionView

我从 Giphy API returns 正确获取的 gif,实际上使用 SwiftGif.

正确加载到 uicollectionview

问题仅在我立即滚动时出现,uicollectionview 加载重复的 gif 或索引不正确的 gif。我知道这可能是延迟渲染 gif 和将 gif 加载到单元格的时间问题。

任何指导将不胜感激,因为异步操作是我仍然不熟悉的东西..

如果下面的代码中有任何标志,特别是为了支持 speed/memory 用法,我们将不胜感激任何处理 gif 的最佳实践。

我试过进行各种检查,比如查看最初传递的 gif url 在加载时是否相同,以及每次触发 cellForItemAt 时都将图像设置为 nil,但无济于事。找不到也能明确解决此问题的现有讨论帖。

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {

@IBOutlet weak var gifCollectionView: UICollectionView!

var gifUrls: [String] = []
var gifImages: [String: UIImage] = [:]

func fetchGiphs() {
    let op = GiphyCore.shared.search("dogs", media: .sticker) { (response, error) in

        guard error == nil else {
            print("Giphy Fetch Error: ", error)
            return
        }

        if let response = response, let data = response.data, let pagination = response.pagination {
            for result in data {
                if let urlStr = result.images?.downsized?.gifUrl {
                    self.gifUrls.append(urlStr)
                }
            }
            if !self.gifUrls.isEmpty {
                DispatchQueue.main.async {
                    self.gifCollectionView.reloadData()
                }
            }
        } else {
            print("No Results Found")
        }
    }
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return gifUrls.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! GifCell

    let passedUrlString = gifUrls[indexPath.item]
    cell.imageView.image = nil

    if let image = gifImages[gifUrls[indexPath.item]] {
        DispatchQueue.main.async {
            cell.imageView.image = image
            cell.activityIndicator.isHidden = true
        }
    } else {
        cell.activityIndicator.isHidden = false
        cell.activityIndicator.startAnimating()
        DispatchQueue.global(qos: .default).async {
            let gifImage = UIImage.gif(url: self.gifUrls[indexPath.item])
            DispatchQueue.main.async {
                if passedUrlString == self.gifUrls[indexPath.item] {
                    cell.activityIndicator.stopAnimating()
                    cell.activityIndicator.isHidden = true
                    cell.imageView.image = gifImage
                    self.gifImages[self.gifUrls[indexPath.item]] = gifImage
                }
            }
        }
    }

    return cell
}
}


class GifCell: UICollectionViewCell {
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!
}

如您所知,当图像加载完成后,单元格可能会被重复使用。

您需要检查它是否被重复使用。您的 passedUrlString == self.gifUrls[indexPath.item] 不适用于此目的。

也许,为每个单元格提供唯一 ID 会奏效:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! GifCell
    let uniqueId = Int.random(in: Int.min...Int.max) //<-practically unique
    cell.tag = uniqueId //<-

    cell.imageView.image = nil

    if let image = gifImages[gifUrls[indexPath.item]] {
        cell.imageView.image = image
        cell.activityIndicator.isHidden = true
    } else {
        cell.activityIndicator.isHidden = false
        cell.activityIndicator.startAnimating()
        DispatchQueue.global(qos: .default).async {
            let gifImage = UIImage.gif(url: self.gifUrls[indexPath.item])
            DispatchQueue.main.async {
                if cell.tag == uniqueId { //<- check `cell.tag` is not changed
                    cell.activityIndicator.stopAnimating()
                    cell.activityIndicator.isHidden = true
                    cell.imageView.image = gifImage
                    self.gifImages[self.gifUrls[indexPath.item]] = gifImage
                }
            }
        }
    }

    return cell
}

假设您没有将 tag 用于其他目的。

请尝试。