TableView Images does not load until scrolling

大憨熊 提交于 2019-12-25 07:49:12

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!