自定义UICollectionViewCell无法从缓存加载问题求助
解决自定义UICollectionViewCell无法从缓存加载图片的问题
这个问题我之前也碰到过,核心原因是UICollectionView的cell复用机制导致旧图片残留,或者图片加载/缓存的逻辑没处理好。给你几个针对性的解决方案:
1. 重写prepareForReuse清空旧内容
当cell被复用时,系统会调用prepareForReuse方法,我们需要在这里清空imageView的旧图片,避免滚动时显示上一个位置的图片。修改你的MomentsCell:
class MomentsCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! override func prepareForReuse() { super.prepareForReuse() // 清空当前图片 imageView.image = nil // 如果用第三方图片库,还可以取消当前未完成的加载请求,比如SDWebImage: // imageView.sd_cancelCurrentImageLoad() } }
2. 使用成熟的图片加载库(推荐)
自己实现缓存很容易踩坑,推荐用SDWebImage、Kingfisher这类成熟的库,它们自带内存+磁盘缓存,并且自动处理cell复用的问题。以SDWebImage为例,修改你的代理方法:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MomentsCell let imageURL = imageURLS[indexPath.row] // 自动处理缓存、异步加载、占位图,以及复用冲突 cell.imageView.sd_setImage(with: URL(string: imageURL), placeholderImage: UIImage(named: "placeholder")) return cell }
3. 自己实现缓存的注意事项
如果一定要手动实现缓存,需要注意以下几点:
- 用
NSCache做内存缓存(比字典更安全,会自动释放内存) - 异步加载图片时,回到主线程设置图片前,要验证cell的indexPath是否和请求的一致
- 用图片URL的字符串作为缓存key,确保唯一性
示例代码:
// 全局缓存实例(可以放在ViewController里或者单独的工具类) private let imageCache = NSCache<NSString, UIImage>() override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MomentsCell let imageURL = imageURLS[indexPath.row] let cacheKey = NSString(string: imageURL) // 先从缓存读取 if let cachedImage = imageCache.object(forKey: cacheKey) { cell.imageView.image = cachedImage return cell } // 缓存不存在,异步加载 DispatchQueue.global(qos: .userInitiated).async { [weak self, weak cell] in guard let url = URL(string: imageURL), let imageData = try? Data(contentsOf: url), let image = UIImage(data: imageData) else { // 加载失败,设置占位图 DispatchQueue.main.async { cell?.imageView.image = UIImage(named: "placeholder") } return } // 存入缓存 self?.imageCache.setObject(image, forKey: cacheKey) // 回到主线程设置图片,同时验证cell是否仍对应当前indexPath DispatchQueue.main.async { guard let currentCell = cell, collectionView.indexPath(for: currentCell) == indexPath else { return } currentCell.imageView.image = image } } return cell }
额外检查项
- 确认你的
reuseIdentifier在注册cell(register(_:forCellWithReuseIdentifier:))和dequeue时完全一致 - 如果用XIB创建cell,检查XIB里的cell的Reuse Identifier是否和代码里的一致
内容的提问来源于stack exchange,提问作者Eduard Lev
相关产品推荐
相关产品推荐

