在UITableViewCell中下载和缓存图像 [英] Download and cache images in UITableViewCell

查看:121
本文介绍了在UITableViewCell中下载和缓存图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:请不要图书馆。这对我来说很重要。此外,对此有各种各样的答案,但我发现没有一个很好地解决了这个问题。请不要标记为重复。提前致谢!



我遇到的问题是,如果你在表格中快速滚动,你会看到旧图像和闪烁。




  • 我读到的问题的解决方案是取消 URLSession
    数据请求。但我不知道如何在正确的地方做到这一点
    和时间。可能还有其他解决方案,但不确定。



这是我到目前为止:



图片缓存类

  class Cache {

static let shared = Cache()

private let cache = NSCache< NSString,UIImage>()
var task = URLSessionDataTask()
var session = URLSession.shared

func imageFor(url:URL,completionHandler:@escaping(image:Image?error:Error?) - > Void){
if let imageInCache = self.cache.object(forKey:url。 absoluteString as NSString){
completionHandler(image:imageInCache,error:nil)
return
}

self.task = self.session.dataTask(with:url ){data,response,error in

if let error = error {
completionHandler(image:nil,error:Error)
return
}

let image = UIImage(data:data!)

self.cache.setObject(image,forKey:url.absoluteString as NSString)
completionHandler(image:image,error:nil)
}

self.task.resume()
}
}

用法

  func tableView(_ tableView:UITableView,cellForRowAt indexPath:IndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier:cell,for:indexPath)
let myImage = images [indexPath.row]

if let imageURL = URL( string:myImage.urlString){
photoImageView.setImage(from:imageURL)
}
return cell
}

有什么想法吗?

解决方案

有几个问题:


  1. 一个可能的闪烁来源是,当你异步更新图像时,你真的想先清除图像视图,所以你看不到前一行重用/出列表视图单元格的图像。在启动异步图像检索之前,请确保将图像视图的图像设置为 nil 。或者,也许将它与占位符逻辑相结合,您将在许多 UIImageView 同步图像检索类别​​中看到。



    <例如:

     扩展名UIImageView {

    func setImage(来自url:URL,占位符: UIImage?= nil){
    image =占位符//使用占位符(或者如果`nil`,删除任何旧图像,在启动异步检索之前

    ImageCache.shared.image(for:url) ){[弱自我]导致
    切换结果{
    case .success(let image):
    self?.image = image

    case .failure:
    中断
    }
    }
    }
    }


  2. 另一个问题是,如果您快速滚动,重用的图像视图可能仍有旧的图像检索请求。当您调用 UIImageView 类别的异步检索方法,你应该取消和与该单元格相关联的先前请求。



    这里的诀窍是,如果你在 UIImageView 扩展名,您不能只创建新的存储属性来跟踪旧请求。因此,您经常使用关联值来跟踪先前的请求。



Note: Please no libraries. This is important for me to learn. Also, there are a variety of answers on this but none that I found solves the issue nicely. Please don't mark as duplicate. Thanks in advance!

The problem I have is that if you scroll really fast in the table, you will see old images and flickering.

  • The solution from the questions I read is to cancel the URLSession data request. But I do not know how to do that at the correct place and time. There might be other solutions but not sure.

This is what I have so far:

Image cache class

class Cache {

    static let shared = Cache()

    private let cache = NSCache<NSString, UIImage>()
    var task = URLSessionDataTask()
    var session = URLSession.shared

    func imageFor(url: URL, completionHandler: @escaping (image: Image? error: Error?) -> Void) {
            if let imageInCache = self.cache.object(forKey: url.absoluteString as NSString)  {
                completionHandler(image: imageInCache, error: nil)
                return
            }

            self.task = self.session.dataTask(with: url) { data, response, error in

                if let error = error {
                    completionHandler(image: nil, error: Error)
                    return
                }

                let image = UIImage(data: data!)

                    self.cache.setObject(image, forKey: url.absoluteString as NSString)
                    completionHandler(image: image, error: nil)
                }

            self.task.resume()
    }
}

Usage

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    let myImage = images[indexPath.row]

    if let imageURL = URL(string: myImage.urlString) {
        photoImageView.setImage(from: imageURL)
    }
    return cell
}

Any thoughts?

解决方案

A couple of issues:

  1. One possible source of flickering is that while you're updating the image asynchronously, you really want to clear the image view first, so you don't see images for prior row of reused/dequeued table view cell. Make sure to set the image view's image to nil before initiating the asynchronous image retrieval. Or, perhaps combine that with "placeholder" logic that you'll see in lots of UIImageView sync image retrieval categories.

    For example:

    extension UIImageView {
    
        func setImage(from url: URL, placeholder: UIImage? = nil) {
            image = placeholder               // use placeholder (or if `nil`, remove any old image, before initiating asynchronous retrieval
    
            ImageCache.shared.image(for: url) { [weak self] result in
                switch result {
                case .success(let image):
                    self?.image = image
    
                case .failure:
                    break
                }
            }
        }
    }
    

  2. The other issue is that if you scroll very quickly, the reused image view may have an old image retrieval request still in progress. You really should, when you call your UIImageView category's async retrieval method, you should cancel and prior request associated with that cell.

    The trick here is that if you're doing this in a UIImageView extension, you can't just create new stored property to keep track of the old request. So you'd often use "associated values" to keep track of prior requests.

这篇关于在UITableViewCell中下载和缓存图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆