在加载图片时滑动 UITableView
导致图片错乱,是 由 Cell 重用机制和异步加载的时序问题 引起的。以下是详细原因和解决方案:
1、原因分析
- Cell 重用机制:
- 当 Cell 滑出屏幕时会被放入重用池,重新显示时用于展示新数据。
- 若异步图片加载完成时,Cell 已被重用给其他位置,图片就会显示到错误的位置。
- 异步加载时序问题:
- 多个异步请求同时进行,后发起的请求可能先完成(如快速滑动时)。
- 导致旧请求覆盖新数据,或图片与内容不匹配。
2、解决方案
方法 1:取消失效的图片请求(推荐)
在 Cell 被重用时,取消其之前的图片请求:
class CustomCell: UITableViewCell {
var currentImageURL: URL? // 记录当前请求的 URL
override func prepareForReuse() {
super.prepareForReuse()
// 1. 取消之前的图片请求
imageView?.kf.cancelDownloadTask() // Kingfisher 示例
// 或 imageView?.sd_cancelCurrentImageLoad() // SDWebImage 示例
// 2. 重置 UI 状态
imageView?.image = nil
currentImageURL = nil
}
}
// CellForRow 中绑定请求
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(...) as! CustomCell
let imageURL = dataArray[indexPath.row].imageURL
// 记录当前请求的 URL
cell.currentImageURL = imageURL
// 加载图片(使用第三方库)
cell.imageView?.kf.setImage(
with: imageURL,
placeholder: UIImage(named: "placeholder"),
completionHandler: { [weak cell] result in
// 加载完成后验证 URL 是否匹配
if cell?.currentImageURL != imageURL {
return // Cell 已重用,丢弃结果
}
// 显示图片
}
)
return cell
}
方法 2:使用第三方库自动处理(推荐)
Kingfisher/SDWebImage 已内置处理逻辑:
// Kingfisher
cell.imageView?.kf.setImage(with: imageURL)
// SDWebImage
cell.imageView?.sd_setImage(with: imageURL)
这些库在 prepareForReuse
中自动取消请求,无需额外代码。
方法 3:同步设置占位图
在加载前重置图片,避免显示旧图片:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(...)
// 先重置为占位图
cell.imageView?.image = UIImage(named: "placeholder")
// 再异步加载
loadImageAsync(url: imageURL) { image in
DispatchQueue.main.async {
// 检查当前 Cell 是否仍显示同一数据
if tableView.indexPath(for: cell) == indexPath {
cell.imageView?.image = image
}
}
}
return cell
}
方法 4:绑定数据模型
通过 Cell 持有数据模型,在回调中校验一致性:
class CustomCell: UITableViewCell {
var data: ItemData? {
didSet {
guard let data = data else { return }
imageView?.image = nil
loadImage(url: data.imageURL) { [weak self] image in
// 检查数据是否仍属于当前 Cell
if self?.data?.id == data.id {
self?.imageView?.image = image
}
}
}
}
}
3、关键实践原则
- 取消机制:在
prepareForReuse()
中取消请求。 - 状态验证:异步回调时检查 Cell 是否仍对应原数据。
- 占位图:加载前重置图片,避免残留。
- 避免阻塞主线程:确保图片解码/缩放等操作在后台进行。
4、补充建议
- 图片压缩/缓存:使用
Kingfisher
或SDWebImage
缓存图片,避免重复请求。
快速滑动优化:
// 限制滚动时加载(SDWebImage 示例)
cell.imageView?.sd_setImage(
with: imageURL,
placeholder: placeholder,
options: [.lowPriority, .progressiveLoad] // 滚动时降低优先级
)
通过以上方法,可有效解决 UITableView 图片错乱问题,提升用户体验。