首页 > 解决方案 > swift:在后台加载图像数据

问题描述

我需要在单元格中加载 100 张图像。如果我在 tableView cellForRowAt 中使用此方法:

cell.cardImageView.image = UIImage(named: "\(indexPath.row + 1).jpg")

并开始快速滚动我的tableView冻结。

我使用这种方法在后台加载修复冻结的图像数据:

func loadImageAsync(imageName: String, completion: @escaping (UIImage) -> ()) {
    DispatchQueue.global(qos: .userInteractive).async {
        guard let image = UIImage(named: imageName) else {return}
        DispatchQueue.main.async {
            completion(image)
        }
    }
}

称之为tableView cellForRowAt

loadImageAsync(imageName: "\(indexPath.row + 1).jpg") { (image) in
        cell.cardImageView.image = image
}

但是我在这种方法中可能会出现一个错误,这样在快速滚动时我可能会看到一段时间的旧图像。如何修复这个错误?

标签: iosswiftuitableview

解决方案


您的细胞正在被重复使用。

当单元格离开屏幕时,它会进入内部UITableView的重用池,当您dequeueReusableCell(withIdentifier:for:) 进入时,tableView(_:cellForRowAt:)您会再次获得此单元格(请参阅名称中的“可重用”)。UITableViewCell了解的生命周期很重要。UITableView不能UITableViewCell为 100 行保留 100 living s,这会降低性能并很快让应用程序没有内存。

为什么你会在你的细胞中看到旧图像? 同样,单元格正在被重用,重用后它们保持旧状态,您需要重置图像,它们不会自行重置。您可以在配置新单元格时执行此操作,或检测单元格何时将被重用。

很简单:

cell.cardImageView.image = nil // reset image
loadImageAsync(imageName: "\(indexPath.row + 1).jpg") { (image) in
        cell.cardImageView.image = image
}

另一种方法是检测重用和重置。在您的单元子类中:

override func prepareForReuse() {
    super.prepareForReuse()
    self.cardImageView.image = nil // reset
}

为什么您在细胞中看到错误的图像?到时completion闭包将图像设置为cardImageViewUITableViewCell已被重用(甚至可能不止一次)。为防止这种情况,您可以测试是否在同一单元格中设置图像,例如,将图像名称与您的单元格一起存储,然后:

// naive approach

let imageName = "\(indexPath.row + 1).jpg"
cell.imageName = imageName
loadImageAsync(imageName: imageName) { (image) in
    guard cell.imageName == imageName else { return } 
    cell.cardImageView.image = image
}

在设计列表时需要注意很多事情,我不会在这里详细介绍。我建议尝试上述方法并调查网络如何处理列表的性能问题。


推荐阅读