2016-11-29 33 views
2

我想增強下面的代碼來緩存圖像,並且只有在之前沒有緩存圖像時才下載它們。我似乎無法找到任何有關如何使用URLSession對象執行此操作的好例子。這個如何在Swift中使用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 
    } 
} 
+1

http://nshipster.com/nsurlcache/ –

+0

無關,你的'如果讓strongSelf = {自我= strongSelf.image圖像}'可以簡化爲'自我?=圖像配image'。 – Rob

+0

FWIW,'NSURLCache'將根據服務器響應頭中提供的內容進行緩存。此外,它還會根據不完整的規則約束緩存,最明顯的是,如果下載量超過總緩存的5%,則無論服務器的標頭說什麼都不會緩存它(這是緩存緩存大小的一個原因如Leo提供的鏈接所述)。 – Rob

回答

1

一個潛在的解決辦法是利用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

1

更新了斯威夫特3

import UIKit 

let imageCache = NSCache<AnyObject, AnyObject>() 

class ImageLoader: UIImageView { 

    var imageURL: URL? 

    let activityIndicator = UIActivityIndicatorView() 

    func loadImageWithUrl(_ url: URL) { 

     // setup activityIndicator... 
     activityIndicator.color = .orange 

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