如何使用swift中的urlsession缓存图像



我想增强下面的代码以缓存图像,仅在以前没有被缓存时才下载它们。我似乎找不到有关如何使用urlsession对象来做到这一点的好例子。

extension UIImageView {
    func loadImageWithURL(_ url: URL) -> URLSessionDownloadTask {
        let session = URLSession.shared
        let downloadTask = session.downloadTask(with: url, completionHandler: { [weak self] url, response, error in
            if error == nil, let url = url,
                let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        if let strongSelf = self {
                            strongSelf.image = image
                        }
                    }
            }
        })
        downloadTask.resume()
        return downloadTask
    }
}

Swift 4

更新
import UIKit
let imageCache = NSCache<AnyObject, AnyObject>()
class ImageLoader: UIImageView {
    var imageURL: URL?
    let activityIndicator = UIActivityIndicatorView()
    func loadImageWithUrl(_ url: URL) {
        // setup activityIndicator...
        activityIndicator.color = .darkGray
        addSubview(activityIndicator)
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        activityIndicator.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        activityIndicator.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
        imageURL = url
        image = nil
        activityIndicator.startAnimating()
        // retrieves image if already available in cache
        if let imageFromCache = imageCache.object(forKey: url as AnyObject) as? UIImage {
            self.image = imageFromCache
            activityIndicator.stopAnimating()
            return
        }
        // image does not available in cache.. so retrieving it from url...
        URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
            if error != nil {
                print(error as Any)
                DispatchQueue.main.async(execute: {
                    self.activityIndicator.stopAnimating()
                })
                return
            }
            DispatchQueue.main.async(execute: {
                if let unwrappedData = data, let imageToCache = UIImage(data: unwrappedData) {
                    if self.imageURL == url {
                        self.image = imageToCache
                    }
                    imageCache.setObject(imageToCache, forKey: url as AnyObject)
                }
                self.activityIndicator.stopAnimating()
            })
        }).resume()
    }
}

用法:

// assign ImageLoader class to your imageView class
let yourImageView: ImageLoader = {
    let iv = ImageLoader()
    iv.frame = CGRect(x: 10, y: 100, width: 300, height: 300)
    iv.backgroundColor = UIColor(red: 0.94, green: 0.94, blue: 0.96, alpha: 1.0)
    iv.contentMode = .scaleAspectFill
    iv.clipsToBounds = true
    return iv
}()

// unwrapped url safely...
   if let strUrl = "https://picsum.photos/300/300".addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
      let imgUrl = URL(string: strUrl) {
      yourImageView.loadImageWithUrl(imgUrl) // call this line for getting image to yourImageView
}

一种潜在的解决方案是利用NSCache照顾缓存。本质上,您要做的是检查您是否已经在本地加载的图像,而不是每次提出请求之前每次获取。

这是我的实现之一 - 它是一个子类,而不是扩展:

class CustomImageView: UIImageView {
    // MARK: - Constants
    let imageCache = NSCache<NSString, AnyObject>()
    // MARK: - Properties
    var imageURLString: String?
    func downloadImageFrom(urlString: String, imageMode: UIViewContentMode) {
        guard let url = URL(string: urlString) else { return }
        downloadImageFrom(url: url, imageMode: imageMode)
    }
    func downloadImageFrom(url: URL, imageMode: UIViewContentMode) {
        contentMode = imageMode
        if let cachedImage = imageCache.object(forKey: url.absoluteString as NSString) as? UIImage {
            self.image = cachedImage
        } else {
            URLSession.shared.dataTask(with: url) { data, response, error in
                guard let data = data, error == nil else { return }
                DispatchQueue.main.async {
                    let imageToCache = UIImage(data: data)
                    self.imageCache.setObject(imageToCache!, forKey: url.absoluteString as NSString)
                    self.image = imageToCache
                }
            }.resume()
        }
    }
}

此外,这是一个有用的资源:https://www.hackingwithswift.com/example-code/system/how-to-cache-data-using-nscache

urlSession dataTask默认情况下将自动缓存图像,只要服务器上的高速缓存设置是正常的,就无需在客户端上执行任何操作。图像是静态资产,因此不会在短时间内发生变化,结果服务器通常设置为" cache-control"。"公共,最大":xxxxx"。URLSession默认缓存策略将在内存和磁盘中缓存图像。但是,它不会缓存大小大于分配给urlcache的磁盘尺寸的5%的图像,并且也不会在背景线程中缓存。

let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
    func loadImageFromUrl(urlString: String)  {
        if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage{
            self.image = imageFromCache
            return
        }
        Alamofire.request(urlString, method: .get).response { (responseData) in
            if let data = responseData.data {
               DispatchQueue.main.async {
                if let imageToCache = UIImage(data: data){
                    imageCache.setObject(imageToCache, forKey: urlString as AnyObject)
                    self.image = imageToCache
                }
            }
        }
    }
 }
}

最新更新