【问题标题】:caching images in collectionViewCell in Swift?在 Swift 的 collectionViewCell 中缓存图像?
【发布时间】:2015-12-11 04:29:24
【问题描述】:

我的 collectionViewCell 中有通过 NSURLRequest 获取和解析的图像,我如何缓存这些图像,这样它们就不必在视图的每一次出现/消失时启动新请求?

这是我获取图像的代码:

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

这是我的 collectionView 代码,用于将图像解析到 collectionViewCell 的:

   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
    }

虽然我收到了对第三方框架的引用,但我没有收到任何关于如何使用我在问题中提供的代码来实现它们的答案,甚至没有收到使用苹果已经实现缓存机制的答案。原因我把代码放在问题中是为了在答案中使用。谢谢。

【问题讨论】:

标签: ios swift caching parse-platform uicollectionviewcell


【解决方案1】:

这是您的收藏视图单元格的示例:

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

别忘了为 NSCache 做一个扩展 像这样:

import Foundation

extension NSCache {
  class var sharedInstance: NSCache<NSString, AnyObject> {
      let cache = NSCache<NSString, AnyObject>()
      return cache
  }
}

【讨论】:

  • 别忘了设置 NSCache 的总成本!
【解决方案2】:

使用 NSCache 和 NSOperationQueue 来管理您的图像加载。 https://stackoverflow.com/a/12721899/5271191 有一篇很好的文章概述了该技术(它是 Objective-C,但 Swift 的技术是相同的。)

【讨论】:

  • 是的,我明白了,这种方法看起来不错,但我有一个小问题
  • 当我添加 var imageDownloadingQueue: NSOperationQueuevar imageCache: NSCache 变量时,它说我的类没有初始化程序.. 为什么会这样?
  • 我的 viewDidLoad self.imageDownloadingQueue = NSOperationQueue() self.imageDownloadingQueue.maxConcurrentOperationCount = 4 self.imageCache = NSCache() 有以下内容...那不是初始化吗?
  • viewDidLoad 在类初始化后调用,因此它与init 函数不同,当您尝试在那里初始化非可选属性时,Swift 会抛出编译错误。您可以在声明期间设置属性来解决此问题,即var imageDownloadingQueue = NSOperationQueue()。否则,将它们设为可选项并在您使用它们时解开它们。
【解决方案3】:

我强烈建议您对 UIImageView 使用干净的替换/扩展,这将对您透明地管理图像的缓存,并避免维护操作队列等不必要的复杂性。

如果内存缓存满足您的需求 - 看看这个 -

https://github.com/nicklockwood/AsyncImageView

如果你想要持久缓存,那么这个就可以了-

https://github.com/rs/SDWebImage

HTH。

【讨论】:

  • 没有用swift写的repos或者教程吗?
  • 您可以将它们包含在您的 swift 项目中,Xcode 将负责创建一个桥头文件,您只需在其中 #import 相关的头文件..
  • 嘿 MikeStrong,看看我的 answer(关于 Swift)
【解决方案4】:

我的 collectionViewCell 中有图像被提取和解析 通过 NSURLRequest,我如何缓存这些图像,这样它们就不必 每次出现/消失时开始一个新的请求 查看?

URL loading system 已经提供了缓存。看看the docs for NSURLCache。如果您需要的资源没有被充分缓存,您可能只需要调整分配给您应用的 URL 缓存的磁盘空间。

您还应该查看与您的资源一起返回的标头(缓存控制、过期等),以确保它们不会阻止缓存。 Here's a short tutorial on cache-related headers.

【讨论】:

    【解决方案5】:

    您应该为此使用专门的框架。我会推荐使用 SDWebImage,它非常过时且不稳定。

    看看这两个与 iOS 平台同步的库:

    • DFImageManager - 用 Objective-C 编写的高级框架,但具有可空性注释(适用于 Swift)。这是一个比 SDWebImage 更好的list of things披露:这是我写的,观点可能有偏见。
    • Kingfisher - 用 Swift 编写的轻量级库。与 SDWebImage 类似,但功能比 SDWebImage 和 DFImageManager 少。

    【讨论】:

    • 我只需要一个缓存机制,我不想改变我使用 ti 请求和解析图像的方法。那么是否可以只实现一个外部缓存?
    • 嗯,这是可能的 :) 但是这个问题已经被多个框架解决了(你也可以将 AFNetworking、AlamofireImage、PINRemoteImage 混合使用)。有很多事情需要考虑,这些框架为您完成了所有繁重的工作。由您决定使用哪一个。抱歉,我不会帮助重新实现这些东西。
    【解决方案6】:

    我使用 swift 2 创建了一个库来请求图像并缓存它。很简单,试试看吧。

    https://github.com/georgehadly/GHImageCaching

    你能做的就是这样,

    viewImg.getCachedImage("geo", URI: NSURL(string: "https://s-media-cache-ak0.pinimg.com/236x/8e/5a/98/8e5a98795dc2c5322cac97343a6cad6d.jpg")!) { (done) -> Void in
            if(done){
                // your extra
            }
        }
    

    如果您想删除所有缓存的图像

    UIImageView.deleteAllCaching()
    

    【讨论】:

      猜你喜欢
      • 2018-06-11
      • 2016-03-23
      • 2017-04-13
      • 2023-03-28
      • 2016-11-30
      • 1970-01-01
      • 2021-01-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多