2016-11-29 2 views
2

Я хотел бы улучшить код ниже, чтобы кэшировать изображения и загружать их только в том случае, если они ранее не были кэшированы. Кажется, я не могу найти хороших примеров того, как использовать объект URLSession для этого.Как кэшировать изображения с помощью URLSession в Swift

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

Unrelated, ваш 'если пусть strongSelf = само {strongSelf.image = образ}' может быть упрощено до 'Я? .image = image'. – Rob

+0

FWIW, 'NSURLCache' будет кэшировать в соответствии с тем, что поставляется в заголовках ответа сервера. Кроме того, он будет ограничивать кеш в соответствии с плохо документированными правилами, особенно если загрузка превышает 5% от общего кеша, она не будет кэшировать его, независимо от того, что говорят заголовки сервера (это одна из причин для уменьшения размера кеша как указано в ссылке Льва). – 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

Обновление для Swift 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 
}