在 UITableViewCell 中下载和缓存图像
Download and cache images in UITableViewCell
注意:请不要图书馆。这对我学习很重要。此外,对此有多种答案,但我发现 none 很好地解决了这个问题。请不要标记为重复。提前致谢!
我遇到的问题是,如果您在 table 中滚动得非常快,您会看到旧图像和闪烁。
- 我阅读的问题的解决方案是取消
URLSession
数据请求。但我不知道如何在正确的地方做到这一点
和时间。可能还有其他解决方案,但不确定。
这是我目前拥有的:
图片缓存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()
}
}
用法
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
}
有什么想法吗?
几个问题:
闪烁的一个可能来源是,当您异步更新图像时,您真的想先清除图像视图,所以您看不到前一行的图像 reused/dequeued table 查看单元格。确保在启动异步图像检索之前将图像视图的 image
设置为 nil
。或者,也许将其与您将在许多 UIImageView
同步图像检索类别中看到的 "placeholder" 逻辑结合起来。
例如:
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
}
}
}
}
另一个问题是,如果滚动速度非常快,重复使用的图像视图可能有一个仍在进行中的旧图像检索请求。当您调用 UIImageView
类别的异步检索方法时,您确实应该取消与该单元格相关联的先前请求。
这里的技巧是,如果您在 UIImageView
扩展中执行此操作,则不能只创建新的存储 属性 来跟踪旧请求。因此,您经常使用 "associated values" 来跟踪之前的请求。
Swift 3:
可以这样避免闪烁:
在public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
中使用以下代码
cell.photoImageView.image = nil //or keep any placeholder here
cell.tag = indexPath.row
let task = URLSession.shared.dataTask(with: imageURL!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
if cell.tag == indexPath.row{
cell.photoImageView.image = UIImage(data: data)
}
}
}
task.resume()
通过检查 cell.tag == indexPath.row
,我们可以确保我们正在更改其图像的 imageview 与图像原本所在的行相同。希望对您有所帮助!
注意:请不要图书馆。这对我学习很重要。此外,对此有多种答案,但我发现 none 很好地解决了这个问题。请不要标记为重复。提前致谢!
我遇到的问题是,如果您在 table 中滚动得非常快,您会看到旧图像和闪烁。
- 我阅读的问题的解决方案是取消
URLSession
数据请求。但我不知道如何在正确的地方做到这一点 和时间。可能还有其他解决方案,但不确定。
这是我目前拥有的:
图片缓存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()
}
}
用法
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
}
有什么想法吗?
几个问题:
闪烁的一个可能来源是,当您异步更新图像时,您真的想先清除图像视图,所以您看不到前一行的图像 reused/dequeued table 查看单元格。确保在启动异步图像检索之前将图像视图的
image
设置为nil
。或者,也许将其与您将在许多UIImageView
同步图像检索类别中看到的 "placeholder" 逻辑结合起来。例如:
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 } } } }
另一个问题是,如果滚动速度非常快,重复使用的图像视图可能有一个仍在进行中的旧图像检索请求。当您调用
UIImageView
类别的异步检索方法时,您确实应该取消与该单元格相关联的先前请求。这里的技巧是,如果您在
UIImageView
扩展中执行此操作,则不能只创建新的存储 属性 来跟踪旧请求。因此,您经常使用 "associated values" 来跟踪之前的请求。
Swift 3:
可以这样避免闪烁:
在public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
cell.photoImageView.image = nil //or keep any placeholder here
cell.tag = indexPath.row
let task = URLSession.shared.dataTask(with: imageURL!) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async() {
if cell.tag == indexPath.row{
cell.photoImageView.image = UIImage(data: data)
}
}
}
task.resume()
通过检查 cell.tag == indexPath.row
,我们可以确保我们正在更改其图像的 imageview 与图像原本所在的行相同。希望对您有所帮助!