Swift 4中含两种自定义Cell的TableView出现单元格复用问题
嘿,这个问题我之前在做TableView多Cell类型开发的时候也踩过类似的坑,咱们来一步步拆解解决它~
问题核心原因
本质上是TableView的Cell复用机制加上图片加载逻辑的疏漏导致的:
- 当Section1的顶部Cell滑出屏幕后,会被放进复用池;当你再滑回来时,系统会从复用池取出这个Cell重新配置
- 如果你的图片加载没有做本地缓存,也没有在复用前处理旧的下载任务,就会触发重复下载
- 结合你提到的场景,大概率是Section2的Cell占用复用池后,Section1的Cell复用状态没有被正确重置导致的
具体解决步骤
1. 给图片请求加上缓存策略
用Alamofire发起请求时,设置缓存策略优先使用本地缓存,避免重复请求网络:
// 构造带缓存策略的请求 let urlRequest = URLRequest( url: imageURL, cachePolicy: .returnCacheDataElseLoad, // 有缓存就用缓存,没有再请求网络 timeoutInterval: 15 ) // 用这个请求发起下载 AF.request(urlRequest).responseData { [weak self] response in guard let data = response.data, let image = UIImage(data: data) else { return } // 更新UI要切回主线程 DispatchQueue.main.async { cell?.iconImageView.image = image } }
2. 复用Cell时取消旧下载任务+重置状态
在你的Section1自定义Cell里,重写prepareForReuse()方法,取消未完成的下载任务,同时重置图片为占位图,避免旧任务完成后覆盖新内容:
class Section1Cell: UITableViewCell { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var iconImageView: UIImageView! // 持有当前Cell的下载任务引用 var imageDownloadTask: DataRequest? override func prepareForReuse() { super.prepareForReuse() // 取消旧的下载任务 imageDownloadTask?.cancel() imageDownloadTask = nil // 重置图片为占位图 iconImageView.image = UIImage(named: "default_placeholder") } }
3. 绑定数据与Cell,加上内存缓存
用NSCache做内存缓存,让滑回的Cell可以直接从内存取图片,不用再走网络:
// 全局或ViewModel里定义内存缓存 let imageMemoryCache = NSCache<NSString, UIImage>() // 在cellForRowAt里配置Cell时 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.section == 0 { let cell = tableView.dequeueReusableCell(withIdentifier: "Section1Cell", for: indexPath) as! Section1Cell let model = yourSection1Data[indexPath.row] // 先设置文本 cell.titleLabel.text = model.title // 先检查内存缓存 let cacheKey = model.imageURL.absoluteString as NSString if let cachedImage = imageMemoryCache.object(forKey: cacheKey) { cell.iconImageView.image = cachedImage return cell } // 没有缓存再发起下载 cell.imageDownloadTask = AF.request(model.imageURL).responseData { [weak cell] response in guard let data = response.data, let image = UIImage(data: data) else { return } // 存入内存缓存 imageMemoryCache.setObject(image, forKey: cacheKey) // 更新UI DispatchQueue.main.async { cell?.iconImageView.image = image } } return cell } else { // 配置Section2的Cell let cell = tableView.dequeueReusableCell(withIdentifier: "Section2Cell", for: indexPath) as! Section2Cell cell.titleLabel.text = "Section 2 Title" cell.emptyImageBackground.backgroundColor = .systemBlue return cell } }
4. 可选:添加磁盘缓存
如果需要更持久的缓存(比如APP重启后依然有效),可以自己实现磁盘缓存逻辑,把下载的图片存到沙盒里,下次加载时先读磁盘再请求网络。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

