Swift 3 : URL Image makes UITableView scroll slow issue

前端 未结 4 742
死守一世寂寞
死守一世寂寞 2021-01-06 21:25

I have an extension to print image URL on UIImageView. But I think the problem is my tableView is so slow because of this extension. I think I need

相关标签:
4条回答
  • 2021-01-06 21:29

    For caching image in background & scroll faster use SDWebImage library

     imageView.sd_setImage(with: URL(string: "http://image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
    

    https://github.com/rs/SDWebImage

    0 讨论(0)
  • 2021-01-06 21:36

    I think, that problem here, that you need to cache your images in table view to have smooth scrolling. Every time your program calls cellForRowAt indexPath it downloads images again. It takes time.

    For caching images you can use libraries like SDWebImage, Kingfisher etc.

    Example of Kingfisher usage:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! CustomCell
    
        cell.yourImageView.kf.setImage(with: URL) 
        // next time, when you will use image with this URL, it will be taken from cache.
    
        //... other code
    }
    

    Hope it helps

    0 讨论(0)
  • 2021-01-06 21:48

    You can use the frameworks as suggested here, but you could also consider "rolling your own" extension as described in this article

    "All" you need to do is:

    1. Use URLSession to download your image, this is done on a background thread so no stutter and slow scrolling.
    2. Once done, update your image view on the main thread.

    Take one

    A first attempt could look something like this:

    func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
        guard let url = URL(string: urlString) else {
            return
        }
    
        //Fetch image
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            //Did we get some data back?
            if let data = data {
                //Yes we did, update the imageview then
                let image = UIImage(data: data)
                DispatchQueue.main.async {
                    imageView.image = image
                }
            }
        }.resume() //remember this one or nothing will happen :)
    }
    

    And you call the method like so:

    loadImage(fromURL: "yourUrlToAnImageHere", toImageView: yourImageView)
    

    Improvement

    If you're up for it, you could add a UIActivityIndicatorView to show the user that "something is loading", something like this:

    func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
        guard let url = URL(string: urlString) else {
            return
        }
    
        //Add activity view
        let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        imageView.addSubview(activityView)
        activityView.frame = imageView.bounds
        activityView.translatesAutoresizingMaskIntoConstraints = false
        activityView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true
        activityView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true
        activityView.startAnimating()
    
        //Fetch image
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            //Done, remove the activityView no matter what
            DispatchQueue.main.async {
                activityView.stopAnimating()
                activityView.removeFromSuperview()
            }
    
            //Did we get some data back?
            if let data = data {
                //Yes we did, update the imageview then
                let image = UIImage(data: data)
                DispatchQueue.main.async {
                    imageView.image = image
                }
            }
        }.resume() //remember this one or nothing will happen :)
    }
    

    Extension

    Another improvement mentioned in the article could be to move this to an extension on UIImageView, like so:

    extension UIImageView {
        func loadImage(fromURL urlString: String) {
            guard let url = URL(string: urlString) else {
                return
            }
    
            let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
            self.addSubview(activityView)
            activityView.frame = self.bounds
            activityView.translatesAutoresizingMaskIntoConstraints = false
            activityView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
            activityView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
            activityView.startAnimating()
    
            URLSession.shared.dataTask(with: url) { (data, response, error) in
                DispatchQueue.main.async {
                    activityView.stopAnimating()
                    activityView.removeFromSuperview()
                }
    
                if let data = data {
                    let image = UIImage(data: data)
                    DispatchQueue.main.async {
                        self.image = image
                    }
                }
            }.resume()
        }
    }
    

    Basically it is the same code as before, but references to imageView has been changed to self.

    And you can use it like this:

    yourImageView.loadImage(fromURL: "yourUrlStringHere")
    

    Granted...including SDWebImage or Kingfisher as a dependency is faster and "just works" most of the time, plus it gives you other benefits such as caching of images and so on. But I hope this example shows that writing your own extension for images isn't that bad...plus you know who to blame when it isn't working ;)

    Hope that helps you.

    0 讨论(0)
  • 2021-01-06 21:48

    Your tableview slow because you load data in current thread which is main thread. You should load data other thread then set image in main thread (Because all UI jobs must be done in main thread). You do not need to use third party library for this just change your extension with this:

    extension UIImageView{
    func setImageFromURl(stringImageUrl url: String){
        if let url = NSURL(string: url) {
            DispatchQueue.global(qos: .default).async{
                if let data = NSData(contentsOf: url as URL) {
                    DispatchQueue.main.async {
                        self.image = UIImage(data: data as Data)
                    }
                }
            }
        }
     }
    }
    
    0 讨论(0)
提交回复
热议问题