为什么UITableView滚动后自定义cell会出现内容显示错误?
问题原因
- Cell复用异步请求未处理:UITableView会复用滑出屏幕的Cell,你在
configure方法中发起的图片异步请求没有做取消逻辑,当Cell被复用到新位置后,旧请求返回仍然会修改当前Cell的内容,造成图文错配。 - 文本赋值逻辑错误:你将名称、地址文本的赋值放在了图片请求成功的回调中,如果图片请求失败、请求响应慢,会出现文本不显示/延迟显示,甚至旧请求返回的文本直接覆盖当前Cell正确文本的问题。
- 复用重置逻辑不全:
prepareForReuse方法中没有清空地址文本、没有取消未完成的图片请求,残留的旧请求和旧内容会干扰新赋值的内容。
修复方案
修改自定义Cell的代码如下:
import UIKit class SearchTableViewCell: UITableViewCell { static let identifier = "SearchTableViewCell" @IBOutlet var loader: UIActivityIndicatorView! @IBOutlet var nameLabel: UILabel! @IBOutlet var addressLabel: UILabel! @IBOutlet var locationImageView: UIImageView! var currentIndexPath: IndexPath? // 新增属性:存储当前正在加载的图片url,用于回调校验 var currentLoadUrl: String? // 新增属性:存储当前图片加载任务,用于复用前取消 var imageLoadTask: URLSessionDataTask? override func awakeFromNib() { super.awakeFromNib() nameLabel.font = UIFont(name: Settings.shared.MAIN_APP_FONT_BOLD, size: 25) nameLabel.textColor = .white nameLabel.numberOfLines = 1 addressLabel.font = UIFont(name: Settings.shared.MAIN_APP_FONT_BOLD, size: 15) addressLabel.textColor = .white addressLabel.numberOfLines = 0 locationImageView.clipsToBounds = true locationImageView.layer.cornerRadius = 8.0 locationImageView.contentMode = .scaleAspectFill } func configure(with urlString: String, name: String, address: String, station: String) { // 优先赋值文本,无需等待图片加载 nameLabel.text = name addressLabel.text = address loader.startAnimating() loader.isHidden = false // 先清空旧图片,避免显示上一次复用的内容 locationImageView.image = nil // 取消上一次未完成的图片加载任务 imageLoadTask?.cancel() guard let url = URL(string: urlString) else { loader.stopAnimating() loader.isHidden = true return } // 记录当前要加载的url currentLoadUrl = urlString let task = URLSession.shared.dataTask(with: url) { [weak self] data, _, error in guard let self = self else { return } // 校验当前加载的url是否和Cell最新要加载的url一致,避免复用错乱 guard self.currentLoadUrl == urlString, let data = data, error == nil, let locationImage = UIImage(data: data) else { DispatchQueue.main.async { self.loader.stopAnimating() self.loader.isHidden = true } return } DispatchQueue.main.async { self.locationImageView.image = locationImage self.loader.stopAnimating() self.loader.isHidden = true } } // 存储当前加载任务 imageLoadTask = task task.resume() } static func nib() -> UINib { return UINib(nibName: "SearchTableViewCell", bundle: nil) } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) } override func prepareForReuse() { super.prepareForReuse() // 复用前取消未完成的请求 imageLoadTask?.cancel() imageLoadTask = nil currentLoadUrl = nil // 清空所有旧内容 nameLabel.text = nil addressLabel.text = nil locationImageView.image = nil currentIndexPath = nil loader.stopAnimating() loader.isHidden = true } }
内容的提问来源于stack exchange,提问作者jo1
相关产品推荐
相关产品推荐

