使用延迟在UITableViewCell上下载延迟图像

时间:2016-09-12 07:58:00

标签: ios swift uitableview lazy-loading

是否安全,推迟单元的异步图像下载是否有意义?这背后的想法是我希望在创建单元格之后执行来自URLSession.shared.image(...)的回调函数,并且只有一次调用cellForRow(at:indexPath)有效,因为我认为不推迟使用此方法在这一点上应该返回nil。

  • URLSession.shared.image是一个私有扩展,它运行数据任务,并且仅当参数中提供的url有效且包含图像时才提供转义回调。

  • setImage(image:animated)是一个私有扩展,允许您使用简单的动画在UIImageView中设置图像。

如果不推迟推迟,请注明替代方案。

感谢任何反馈,谢谢!

override func tableView(_ tableView: UITableView, cellForRowAt
    indexPath: IndexPath) -> UITableViewCell {

    let cell = baseCell as! MyCell
    let datum = data[indexPath.row]
    cell.imageView.setImage(placeholderImage, for: .normal)

    defer {
        URLSession.shared.image(with: datum.previewURL) { image, isCached in
            if let cell = tableView.cellForRow(at: indexPath) as? MyCell {
                cell.imageView.setImage(image, animated: !isCached)
            }
        }
    }

    return cell
}

2 个答案:

答案 0 :(得分:1)

NSHipster有一篇关于如何/何时使用延迟的好文章,here

我不会以这种方式使用defer。延迟的目的是在一个代码块中清理/释放内存,而不是将其分散到许多出口点。

考虑在整个函数中使用多个保护语句,并且必须在每个函数中释放内存。

您不应该使用它来简单地在事后添加其他代码。

如@jagveer所述,已有许多第三方库已经这样做,例如SDWebImage缓存。 AFNetworking和AlamoFire也有相同的内置。当它已经完成时,无需重新发明轮子。

答案 1 :(得分:0)

在这种情况下,defer无效。 defer设置要在范围退出时调用的块,您正在立即执行。

我认为您希望使用Dispatch安排块在另一个线程中运行。您需要返回主线程来更新UI。

由于这可能在以后发生,您需要确保该单元格仍然用于相同的条目,并且由于用户已进一步滚动而未被重复使用。再次获取单元格并不是一个好主意,如果它已被重复使用,因为您最终会再次触发初始调用。我通常会在自定义UITableViewCell类中添加一些标识符以进行检查。

此外,您还没有创建单元格,只是从其他变量中获取单元格。如果有多个单元格,这可能是个问题。

override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "base") as! MyCell
    cell.row = indexPath.row
    let datum = data[indexPath.row]
    cell.imageView.setImage(placeholderImage, for: .normal)

    DispatchQueue.global().async {
        // Runs in a background thread
        URLSession.shared.image(with: datum.previewURL) { image, isCached in
            DispatchQueue.main.async {
                // Runs in the main thread; safe for updating the UI
                // but check this cell is still being used for the same index path first!
                if cell.row == indexPath.row {
                    cell.imageView.setImage(image, animated: !isCached)
                }
            }
        }
    }

    return cell
}