在Swift 5中使用nscache在uicollectionview中缓存图像

    科技2025-03-06  20

    Today, we will learn how to use NSCache in Swift to cache images inside a UICollectionView.

    今天,我们将学习如何使用NSCache斯威夫特缓存图像内UICollectionView 。

    In short, this is what you will master in this tutorial:

    简而言之,这是您将在本教程中掌握的内容:

    Understanding what an NSCache is and how to use it.

    了解什么是NSCache以及如何使用它。

    Creating a centered UICollectionView layout.

    创建居中的UICollectionView布局。

    Executing image-loading tasks on a background thread.

    在后台线程上执行图像加载任务。

    Understanding why it is better to use an NSCache instead of a plain Dictionary when we want to cache heavy objects.

    了解当我们要缓存重对象时为什么最好使用NSCache而不是普通的Dictionary 。

    This is what we will have at the end of this article:

    这是本文结尾处的内容:

    The full source code of the project is available at the bottom of the article.

    该项目的完整源代码可在文章底部找到。

    Without further ado, let’s get started.

    事不宜迟,让我们开始吧。

    开始吧 (Let’s Start)

    First, let’s create a PhotoCollectionViewCell that simply contains a UIImageView:

    首先,让我们创建一个仅包含UIImageView的PhotoCollectionViewCell :

    import UIKit class PhotoCollectionViewCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) self.contentView.addSubview(imageView) NSLayoutConstraint.activate([ imageView.topAnchor .constraint(equalTo: self.contentView.topAnchor), imageView.leftAnchor .constraint(equalTo: self.contentView.leftAnchor), imageView.rightAnchor .constraint(equalTo: self.contentView.rightAnchor), imageView.bottomAnchor .constraint(equalTo: self.contentView.bottomAnchor), ]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func prepareForReuse() { super.prepareForReuse() self.imageView.image = nil } let imageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFit imageView.clipsToBounds = true imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() }

    We override the prepareForReuse() method to clear the UIImageView once a cell is preparing to appear on the screen.

    一旦单元格准备显示在屏幕上,我们将重写prepareForReuse()方法以清除UIImageView 。

    Now, let’s add a UICollectionViewController subclass, PhotosViewController, and implement its UICollectionView data source methods:

    现在,让我们添加一个UICollectionViewController子类PhotosViewController ,并实现其UICollectionView数据源方法:

    import UIKit class PhotosViewController: UICollectionViewController { // MARK: - Lifecycle Methods override func viewDidLoad() { super.viewDidLoad() if #available(iOS 13.0, *) { self.overrideUserInterfaceStyle = .light } self.collectionView.backgroundColor = .white self.collectionView.register(PhotoCollectionViewCell.self, forCellWithReuseIdentifier: "Cell") } // MARK: - UICollectionView Data Source override func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 40 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! PhotoCollectionViewCell return cell } }

    As we can see, we use here our previously defined PhotoCollectionViewCell. We also tell the collection view to have 40 items in total.

    如我们所见,这里我们使用先前定义的PhotoCollectionViewCell 。 我们还告诉集合视图总共有40个项目。

    Now we need to specify the size and positioning of the collection view’s cells. We need to create a UICollectionViewFlowLayout as follows:

    现在,我们需要指定集合视图的单元格的大小和位置。 我们需要创建一个UICollectionViewFlowLayout ,如下所示:

    import UIKit class PhotosCollectionViewLayout: UICollectionViewFlowLayout { override init() { super.init() let screenWidth = UIScreen.main.bounds.width let widthHeightConstant = UIScreen.main.bounds.width / 2.2 self.itemSize = CGSize(width: widthHeightConstant, height: widthHeightConstant) let numberOfCellsInRow = floor(screenWidth / widthHeightConstant) let inset = (screenWidth - (numberOfCellsInRow * widthHeightConstant)) / (numberOfCellsInRow + 1) self.sectionInset = .init(top: inset, left: inset, bottom: inset, right: inset) self.minimumInteritemSpacing = inset self.minimumLineSpacing = inset self.scrollDirection = .vertical } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }

    We want our items to be square and about half the width of the screen. We also want them to be centered so that we have a small inset between each of them. For this reason, we create the inset property and pass it as a parameter inside the sectionInset property.

    我们希望我们的项目是正方形的,大约是屏幕宽度的一半。 我们还希望它们居中,以使它们之间有较小的凹痕。 因此,我们创建inset属性并将其作为参数传递给sectionInset属性中。

    As a result, we have a centered UICollectionView layout.

    结果,我们有一个居中的UICollectionView布局。

    With the UI part done, now let’s get to the crux of this article: loading, caching, and displaying UIImages using an NSCache.

    与UI部分完成的,现在让我们开始这篇文章的症结所在:加载,缓存,并显示UIImages使用NSCache 。

    加载和缓存图像 (Loading and Caching Images)

    First, let’s define two properties inside the PhotosViewController — an NSCache and a DispatchQueue:

    首先,让我们在PhotosViewController定义两个属性NSCache和DispatchQueue :

    import UIKit class PhotosViewController: UICollectionViewController { private let cache = NSCache<NSNumber, UIImage>() private let utilityQueue = DispatchQueue.global(qos: .utility) // MARK: - Lifecycle Methods override func viewDidLoad() { super.viewDidLoad() ... } // MARK: - UICollectionView Data Source ... }

    We specify NSNumber as the type for keys (NSCache is a similar concept to a dictionary) and UIImage as the type for objects. I have chosen to use NSNumber because we want to store indexes of cells as keys. This will make it convenient to search for an image at a specific IndexPath item.

    我们将NSNumber指定为键的类型( NSCache是与字典相似的概念),并将UIImage指定为对象的类型。 我选择使用NSNumber是因为我们想将单元格的索引存储为键。 这将使在特定IndexPath项上搜索图像变得很方便。

    The utilityQueue is needed to perform image-loading tasks on a background thread.

    需要使用utilityQueue在后台线程上执行图像加载任务。

    Now we should add the loadImage(completion:) method, which will load a UIImage at a URL and then pass it inside the completion handler:

    现在,我们应该添加loadImage(completion:)方法,该方法将在URL加载UIImage ,然后将其传递到完成处理程序中:

    import UIKit class PhotosViewController: UICollectionViewController { private let cache = NSCache<NSNumber, UIImage>() private let utilityQueue = DispatchQueue.global(qos: .utility) // MARK: - Lifecycle Methods override func viewDidLoad() { super.viewDidLoad() ... } // MARK: - Image Loading private func loadImage(completion: @escaping (UIImage?) -> ()) { utilityQueue.async { let url = URL(string: "https://picsum.photos/200")! guard let data = try? Data(contentsOf: url) else { return } let image = UIImage(data: data) DispatchQueue.main.async { completion(image) } } } // MARK: - UICollectionView Data Source ... }

    Finally, we can use this method inside the willDisplayCell() delegate method as follows:

    最后,我们可以在willDisplayCell()委托方法中使用此方法,如下所示:

    import UIKit class PhotosViewController: UICollectionViewController { private let cache = NSCache<NSNumber, UIImage>() private let utilityQueue = DispatchQueue.global(qos: .utility) // MARK: - Lifecycle Methods override func viewDidLoad() { super.viewDidLoad() ... } // MARK: - Image Loading ... // MARK: - UICollectionView Data Source ... // MARK: - UICollectionView Delegate override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { // 1 guard let cell = cell as? PhotoCollectionViewCell else { return } // 2 let itemNumber = NSNumber(value: indexPath.item) // 3 if let cachedImage = self.cache.object(forKey: itemNumber) { print("Using a cached image for item: \(itemNumber)") cell.imageView.image = cachedImage } else { // 4 self.loadImage { [weak self] (image) in guard let self = self, let image = image else { return } cell.imageView.image = image // 5 self.cache.setObject(image, forKey: itemNumber) } } } }

    Here is a breakdown of the steps:

    以下是步骤的细分:

    Ensure that the displayed cell is a PhotoCollectionViewCell.

    确保显示的单元格是PhotoCollectionViewCell 。

    Obtain the item number of the cell.

    获取单元格的项目号。

    If a cached image is found at the item number, retrieve it and assign it to the UIImageView.

    如果在项目编号处找到了缓存的图像,请对其进行检索并将其分配给UIImageView 。

    If there is no cached image at the item number, launch the image-loading task. Upon image retrieval, assign the image to the UIImageView.

    如果在项目号处没有缓存的图像,请启动图像加载任务。 检索图像后,将图像分配给UIImageView 。

    Store the loaded image inside the NSCache for future reuse.

    将加载的映像存储在NSCache以备将来重用。

    Great! Now that we have a handy caching mechanism, we no longer need to re-download an image once a particular cell is again on the screen.

    大! 现在我们有了一个方便的缓存机制,一旦特定单元格再次出现在屏幕上,我们就不再需要重新下载图像。

    NSCache的好处 (NSCache Benefits)

    Here are the benefits of using NSCache over a plain Dictionary:

    这是在普通Dictionary上使用NSCache的好处:

    If an app’s memory is low, some items are automatically removed from the NSCache. You don’t need to handle this logic yourself.

    如果应用程序的内存不足,则会自动从NSCache删除某些项目。 您无需自己处理此逻辑。

    NSCache is thread-safe, meaning that you should not fear race conditions when accessing it from multiple threads.

    NSCache是线程安全的,这意味着从多个线程访问它时,您不必担心竞争状况。

    We can specify the desired maximum size (in bytes) of the cache and the number of objects put into it:

    我们可以指定所需的最大缓存大小(以字节为单位)以及放入其中的对象数: cache.countLimit = 75 (75 images)cache.totalCostLimit = 50 * 1024 * 1024 (50 MB)

    If the total number or size of objects put into the cache exceeds the desired limit, the OS may start removing objects until the total value is below the desired maximum.

    如果放入缓存中的对象总数或大小超过所需的限制,则OS 可能会开始删除对象,直到总值低于所需的最大值为止。

    资源资源 (Resources)

    The source code of the project is available on GitHub:

    该项目的源代码可在GitHub上找到:

    结语 (Wrapping Up)

    To learn more about NSCache and its object-removing behavior, see the official Apple documentation.

    要了解有关NSCache及其对象删除行为的更多信息,请参阅Apple官方文档 。

    I hope you found this tutorial useful. Thanks for reading!

    希望本教程对您有所帮助。 谢谢阅读!

    翻译自: https://medium.com/better-programming/cache-images-in-a-uicollectionview-using-nscache-in-swift-5-b70ebf090521

    相关资源:四史答题软件安装包exe
    Processed: 0.031, SQL: 8