Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

caching images in collectionViewCell in Swift?

I have images in my collectionViewCell's that are fetched and parsed via NSURLRequest, how do I cache these images so they don't have to start a new request with every single appearance/disappearance of the view?

here is my code that fetches the images:

class funnyPicture: NSObject {

    var pfPicture : PFObject
    var coverImage : UIImage!

    init(pfPicture: PFObject) {
        self.pfPicture = pfPicture
    }

    func fetchCoverImage(completion: (image: UIImage?, error: NSError?) -> Void) {
        let urlString = self.pfPicture["funnyPictures"] as! String
        let url = NSURL(string: urlString)
        let request = NSURLRequest(URL: url!)
        let queue = dispatch_get_main_queue()

        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse?, data: NSData?, error: NSError?) in
            if error == nil {
                self.coverImage = UIImage(data: data!)
                completion(image: self.coverImage, error: nil)
            } else {
                completion(image: nil, error: error)
            }
        }
    }
}

and here is my collectionView code that parse the images to the collectionViewCell's:

   override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! MyCollectionViewCell

        // Configure the cell
        let book = self.books[indexPath.row]
        let coverImage = book.coverImage
        if coverImage == nil {
            book.fetchCoverImage({ (image, error) -> Void in
                if self.collectionView != nil {
                    collectionView.reloadItemsAtIndexPaths([indexPath])
                }
            })
        } else {
            dispatch_async(dispatch_get_main_queue()){
                let imageView = cell.imageView
                imageView.image = book.coverImage
            }
        };
        if book.coverImage == nil {
            cell.imageView.userInteractionEnabled = false
            cell.userInteractionEnabled = false
        }else {
            cell.imageView.userInteractionEnabled = true
            cell.userInteractionEnabled = true

        }

        return cell
    }

While I've received references to third party frameworks, I haven't received any answer on how to implement them with the code I have provided in the question, or even an answer using apples already implemented caching mechanism.. The reason I put the code in the question was for use in an answer.. Thank you.

like image 481
Mike Strong Avatar asked Mar 15 '23 19:03

Mike Strong


2 Answers

Here is an example for your collection view cell:

import UIKit

let imageCache = NSCache<AnyObject, AnyObject>.sharedInstance

class myCell: UICollectionViewCell {

    @IBOutlet public weak var myImageView: UIImageView?

    private var imageUrlString: String?

    private var downloadTask: URLSessionDownloadTask?
    public var imageURL: URL? {
        didSet {
            self.downloadItemImageForSearchResult(imageURL: imageURL)
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        // Initialization code
    }

    public func downloadItemImageForSearchResult(imageURL: URL?) {

        if let urlOfImage = imageURL {
            if let cachedImage = imageCache.object(forKey: urlOfImage.absoluteString as NSString){
            self.myImageView!.image = cachedImage as? UIImage
        } else {
            let session = URLSession.shared
            self.downloadTask = session.downloadTask(
                with: urlOfImage as URL, completionHandler: { [weak self] url, response, error in
                    if error == nil, let url = url, let data = NSData(contentsOf: url), let image = UIImage(data: data as Data) {

                        DispatchQueue.main.async() {

                            let imageToCache = image

                            if let strongSelf = self, let imageView = strongSelf.myImageView {

                                imageView.image = imageToCache

                                imageCache.setObject(imageToCache, forKey: urlOfImage.absoluteString as NSString , cost: 1)
                            }
                        }
                    } else {
                        //print("ERROR \(error?.localizedDescription)")
                    }
            })
            self.downloadTask!.resume()
        }
      }
    }

    override public func prepareForReuse() {
      self.downloadTask?.cancel()
      myImageView?.image = UIImage(named: "ImagePlaceholder")
    }

    deinit {
      self.downloadTask?.cancel()
      myImageView?.image = nil
    }
}

Don't forget to make an extension for NSCache Like this:

import Foundation

extension NSCache {
  class var sharedInstance: NSCache<NSString, AnyObject> {
      let cache = NSCache<NSString, AnyObject>()
      return cache
  }
}
like image 131
Illya Krit Avatar answered Mar 31 '23 07:03

Illya Krit


Use NSCache and NSOperationQueue to manage your image loading. There's a good post outlining the technique at https://stackoverflow.com/a/12721899/5271191 (It's Objective-C, but the technique is the same for Swift.)

like image 32
Dan Nichols Avatar answered Mar 31 '23 05:03

Dan Nichols