问题
I'm using Firebase
to load my images into my cell's ImageView
but! there is a problem. I'm using caching mechanism for preventing images to redownload when reusable cells
loads but the images do not load until i've scroll my TableView
.
Here is the code;
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
}
I think the problem is, when URLSession
downloaded the image and set it into ImageView
, but the loaded image is not showing up until the indexpath.row
called back when scrolling up and down.
I realized that the cause of it is this if statement;
if self.imageURLString == urlString
But if i remove it, the cells getting worse coz of reusable cell's behaviour. Some cells still showing the old cached images.
Really does not aware of what is going on. Was not expecting that TableViews
are kind of complex structure as it is.
Thanks.
回答1:
I am guessing that imageURLString is a field of your class (a UIViewController maybe).
That means that imageURLString = urlString
will change its value each time you go in your method.
When the table is rendered, it will call the method for each cell, each time triggerring a download if the image needed is not in the cache. But after those calls, the value of imageURLString
will be the url used for the last cell, so only this cell will actually use the image wich has been loaded.
If you want to fix your code, you should have one imageURLString
per cell, for example by moving the field to the cell class (create a UITableViewCell
subclass in this case), and replace imageURLString
and self.imageURLString
by cell.imageURLString
回答2:
I believe the imageCache must be global and should not placed within inside the tableView(_:cellForRowAt:).
Instead, tuck everything in a Custom UIImageView Class except for a var imageCache = String: UIImage. Leave that global.
Change all UIImageView's that will use the loadImage(urlString: String) function that is inside this CustomImageView class to a CustomImageView!.
Example:
@IBOutlet weak var userImage: UIImageView!
change to
@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()
}
}
Example of calling the load method:
cell.userImage.loadImage(urlString: userImageUrl)
回答3:
I solve it with the following method which is learned from an iOS tutorial on YouTube.
We can create a CustomImageView
class, and let imageCache = NSCache<AnyObject, AnyObject>()
to save the image Cache.When scrolling the tableview,if there is an imageCache for the imageView, we use it directly.But if it doesn't exsit, we will download the image with image URL string and save it.
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()
}
}
And in the custom cell Class file, we can use it as follows:
if let imageUrlString = thumbnailUrlString {
thumbnailImageView.loadImageWithString(imageUrlString)
}
Note that 1,thumbnailImageView :CustomImageView 2, thumbnailUrlString is optional
来源:https://stackoverflow.com/questions/41224294/tableview-images-does-not-load-until-scrolling