如何使用UIImageViewExtension与Swift异步加载图像并防止重复图像或错误图像加载到单元格

时间:2019-05-11 23:07:38

标签: ios swift uiimageview urlsession kingfisher

我正在Swift 4中开发图像加载库,类似于Kingfisher,并具有一些扩展功能以支持将URL中的图像加载到UIImageView中。

因此,我可以在具有UIImageView的UICollection或UITableview单元格上使用此扩展:

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: 
  "collectioncell", for: indexPath)

    if let normal = cell as? CollectionViewCell {
    normal.imagview.loadImage(fromURL:imageURLstrings[indexPath.row])
  }

基本上这是我的扩展代码,实现基本的URLSession dataTask和缓存

public extension UIImageView {
public func loadImage(fromURL url: String) {
    guard let imageURL = URL(string: url) else {
        return
    }

    let cache =  URLCache.shared
    let request = URLRequest(url: imageURL)
    DispatchQueue.global(qos: .userInitiated).async {
        if let data = cache.cachedResponse(for: request)?.data, let 
           image = UIImage(data: data) {
             DispatchQueue.main.async {
                self.image = image
            }
        } else {
            URLSession.shared.dataTask(with: request, 
       completionHandler: { (data, response, error) in

            print(response.debugDescription)
                print(data?.base64EncodedData() as Any)
                print(error.debugDescription)
                if let data = data, let response = response, ((response as? HTTPURLResponse)?.statusCode ?? 500) < 300, let image = UIImage(data: data) {
                    let cachedData = CachedURLResponse(response: response, data: data)
                    cache.storeCachedResponse(cachedData, for: request)
                    DispatchQueue.main.async {
                       self.image = image
                    }
                }
            }).resume()
        }
    }
}



 }

结论我发现,有时,如果我的URL损坏或得到404,或者即使我在完全下载所有图像之前滚动UICollectionView,图像也会加载到错误的单元格中,或者有时我会在collectionView中找到重复的图像,但这如果我使用Kingfisher不会发生。

This is my results

This is kingfishers

如何防止扩展程序将错误的图像加载到单元格中或复制图像?

1 个答案:

答案 0 :(得分:1)

您正在异步更新图像视图,无论该图像视图是否已用于其他单元格。

当您开始新的图像视图请求时,假设您没有立即在缓存中找到图像,那么在开始网络请求之前,您应该(a)删除任何先前的图像(如Brandon建议的那样); (b)可能会加载一个占位符图像或UIActivityIndicatorView; (c)取消对该图像视图的任何先前图像请求。只有这样,您才可以开始新的请求。

关于在扩展中保存对先前请求的引用的方式,您无法添加存储的属性,但是可以在启动会话时使用objc_setAssociatedObject保存会话任务,将其设置为请求完成时,nil,检索会话对象时,objc_getAssociatedObject,以查看是否需要取消前一个对象。

(顺便说一下,Kingfisher将此关联的对象逻辑包装在它们的computed property for the task identifier中。这是保存和检索此任务标识符的一种好方法。


对于失败的请求,您正在执行不受限制的图像请求这一事实可能会导致该问题。滚动一下,您的请求将积压并超时。进行取消(请参见上文)将减少该问题,但最终可能仍会发生。如果在解决上述问题后仍继续使请求失败,则可能需要限制并发请求的数量。一种常见的解决方案是将请求包装在异步Operation子类中,然后使用OperationQueue将它们添加到maxConcurrentOperationCount中。或者,如果您正在寻找一种便宜又开朗的解决方案,则可以尝试提高请求中的超时阈值。