Swift 3 : URL Image makes UITableView scroll slow issue

前端 未结 4 776
死守一世寂寞
死守一世寂寞 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: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.

提交回复
热议问题