我正在使用Firebase
将我的图片加载到我的手机ImageView
中,但是!这儿存在一个问题。我在reusable cells
加载时使用缓存机制阻止图片重新下载,但在我滚动TableView
之前图片无法加载。
这是代码;
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! TableViewCell
cell.imgv.image = nil
if let urlString = self.filteredFlats[indexPath.row].flatThumbnailImage?.imageDownloadURL
{
imageURLString = urlString
let url = URL(string: urlString)
if let imagefromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage
{
DispatchQueue.main.async {
cell.imgv.image = imagefromCache
return
}
}
else
{
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
DispatchQueue.main.async {
let imagetoCache = UIImage(data:data!)
if self.imageURLString == urlString
{
cell.imgv.image = imagetoCache
}
self.imageCache.setObject(imagetoCache!, forKey: urlString as AnyObject)
}
}).resume()
}
}
return cell
}
我认为问题在于,当URLSession
下载图片并将其设置为ImageView
时,加载的图片在向上和向下滚动时调用indexpath.row
时才会显示
我意识到它的原因就是这个if语句;
if self.imageURLString == urlString
但是,如果我删除它,细胞会变得更糟,因为可重复使用的细胞的行为。有些单元格仍然显示旧的缓存图像。
真的不知道发生了什么。没想到TableViews
是一种复杂的结构。
感谢。
答案 0 :(得分:0)
我猜测imageURLString是你的类的一个字段(也许是UIViewController)。
这意味着每次进入方法时imageURLString = urlString
都会更改其值。
当表格呈现时,它将为每个单元格调用方法,如果所需图像不在缓存中,则每次触发下载。但是在这些调用之后,imageURLString
的值将是用于最后一个单元格的URL,因此只有这个单元格才会实际使用已加载的图像。
如果要修复代码,每个单元格应该有一个imageURLString
,例如将字段移动到单元格类(在这种情况下创建UITableViewCell
子类),并替换{ {1}}和imageURLString
self.imageURLString
答案 1 :(得分:0)
我相信imageCache必须是全局的,不应该放在tableView(_:cellForRowAt :)中。
相反,除了var imageCache = String:UIImage之外,将所有内容都放在Custom UIImageView Class中。把它留给全球。
将所有将使用此CustomImageView类中的loadImage(urlString:String)函数的UIImageView更改为CustomImageView!。
示例:
@IBOutlet weak var userImage:UIImageView!
更改为
@IBOutlet weak var userImage:CustomImageView!
import UIKit
// global var
var imageCache = [String: UIImage]()
class CustomImageView: UIImageView {
var lastURLUsedToLoadImage: String?
func loadImage(urlString: String) {
lastURLUsedToLoadImage = urlString
self.image = nil
if let cachedImage = imageCache[urlString] {
self.image = cachedImage
return
}
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
if let err = err {
print("Failed to fetch post image:", err)
return
}
if url.absoluteString != self.lastURLUsedToLoadImage {
return
}
guard let imageData = data else { return }
let photoImage = UIImage(data: imageData)
imageCache[url.absoluteString] = photoImage
// update the UI
DispatchQueue.main.async {
self.image = photoImage
}
}.resume()
}
}
调用load方法的示例:
cell.userImage.loadImage(urlString: userImageUrl)
答案 2 :(得分:0)
我使用从YouTube上的iOS教程中学习的以下方法解决它。
我们可以创建一个CustomImageView
类,let imageCache = NSCache<AnyObject, AnyObject>()
来保存图像Cache。当滚动tableview时,如果imageView有一个imageCache,我们直接使用它。但是如果它不存在,我们将下载带有图片URL字符串的图片并保存。
import UIKit
let imageCache = NSCache<AnyObject, AnyObject>()
class CustomImageView: UIImageView {
var imageUrlString :String?
func loadImageWithString(_ urlString:String){
imageUrlString = urlString
let request = URLRequest(url: URL(string:urlString)!)
self.image = nil
//imageCache exist,and use it directly
if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = imageFromCache
}
//imageCache doesn't exist,then download the image and save it
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription as Any)
return
}
DispatchQueue.main.async() {
let imageToCache = UIImage(data: data)
imageCache.setObject(imageToCache!, forKey: urlString as String as AnyObject )
if self.imageUrlString == urlString{
self.image = imageToCache
}
}
}).resume()
}
}
在自定义单元格类文件中,我们可以按如下方式使用它:
if let imageUrlString = thumbnailUrlString {
thumbnailImageView.loadImageWithString(imageUrlString)
}
注意1,thumbnailImageView:CustomImageView 2,thumbnailUrlString是可选的