Swift 3 : URL 图片使 UITableView 滚动缓慢的问题

Swift 3 : URL Image makes UITableView scroll slow issue

我有一个扩展程序可以在 UIImageView 上打印图像 URL。但我认为问题是我的 tableView 因为这个扩展太慢了。我想我需要为它打开线程。我怎样才能在这个扩展中创建一个线程,或者你知道解决这个问题的另一种方法吗?

我的代码:

extension UIImageView{

    func setImageFromURl(stringImageUrl url: String){

        if let url = NSURL(string: url) {
            if let data = NSData(contentsOf: url as URL) {
                self.image = UIImage(data: data as Data)
            }
        }
    }
}

我认为,这里的问题是,您需要将图像缓存在 table view 中才能平滑滚动。每次您的程序调用 cellForRowAt indexPath 时,它都会再次下载图像。这需要时间。

对于缓存图像,您可以使用 SDWebImageKingfisher 等库

Kingfisher 用法示例:

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
}

希望对您有所帮助

要在后台缓存图像并更快地滚动,请使用 SDWebImage

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

https://github.com/rs/SDWebImage

可以使用此处建议的框架,但您也可以考虑 "rolling your own" 扩展,如 this article

中所述

"All"您需要做的是:

  1. 使用 URLSession 下载您的图片,这是在后台线程上完成的,因此不会卡顿和缓慢滚动。
  2. 完成后,在主线程上更新图像视图。

拿一个

第一次尝试可能看起来像这样:

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 :)
}

然后你像这样调用方法:

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

改进

如果您愿意,可以添加 UIActivityIndicatorView 向用户显示 "something is loading",如下所示:

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 :)
}

扩展

文章中提到的另一项改进可能是将其移至 UIImageView 上的 extension,如下所示:

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()
    }
}

基本上它与以前的代码相同,但对 imageView 的引用已更改为 self

你可以这样使用它:

yourImageView.loadImage(fromURL: "yourUrlStringHere")

当然...包括 SDWebImage 或 Kingfisher 作为依赖项会更快,并且 "just works" 大多数时候,此外它还为您提供其他好处,例如图像缓存等。但我希望这个例子表明,为图像编写自己的扩展并没有那么糟糕......而且当它不起作用时你知道应该责怪谁;)

希望对您有所帮助。

你的表视图很慢,因为你在主线程的当前线程中加载数据。您应该加载其他线程的数据,然后在主线程中设置图像(因为所有 UI 作业必须在主线程中完成)。您不需要为此使用第三方库,只需更改您的扩展名:

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)
                }
            }
        }
    }
 }
}