【问题标题】:Loading Aync Gifs to Scrolling CollectionView将异步 Gif 加载到滚动 CollectionView
【发布时间】:2019-10-03 04:01:38
【问题描述】:

我从 Giphy API 获取的 gif 正确返回,实际上使用 SwiftGif 正确加载到 uicollectionview

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

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

如果下面的代码中有任何标志,特别是为了支持速度/内存使用,任何处理 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!
}

【问题讨论】:

    标签: ios swift asynchronous uicollectionview gif


    【解决方案1】:

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

    您需要检查它是否被重复使用。您的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 用于其他目的。

    请尝试。

    【讨论】:

    • 它有效!但是......由于某种原因,如果您尝试滚动并且我不确定为什么+如何解决,那么collectionView会在最初加载时冻结?这是一个线程问题吗,如果是这样,我认为 DispatchQueue.global.async 解决了这个问题?希望你能帮助我更好地理解和优化明显的劳动性能..@OOPer
    • @Chris,感谢您的报告。但是我找不到您的确切原因。一个可能的原因可能是您的应用程序同时发送了太多请求,这可能发生在您的原始代码中。除非有关正在发生的事情的更多信息,否则我无法为您提供帮助。
    • 感谢您的快速回复!我很乐意提供我能提供的更多背景信息!上面的代码虽然基本上是所有的代码...从 gif url 字符串的 Giphy API 获取,然后通过 cellForItem 下载 gif 并将其分配给单元格的 uiimageview。 @OOPer
    • @Chris,据我检查 SwiftGif 的源代码,它在指针处理方面存在一些错误,这可能是导致问题的原因。尝试使用 SwiftGif 删除该部分,看看会发生什么。
    • 我删除了它,collectionView 现在可以正常滚动,但这可能是因为不再执行连续的密集下载任务。我使用 SwiftGif 下载和创建 gif 图像,这里有解决方法吗?我不确定如何更好地优化工作量,因为我确实需要输出的 gif 图像。@OOPer
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-27
    • 2017-01-22
    • 1970-01-01
    相关资源
    最近更新 更多