【问题标题】:Updating cell height after image downloads图像下载后更新单元格高度
【发布时间】:2016-03-02 17:11:58
【问题描述】:

我在UITableView 中显示一些文本和图像。图像首先被下载。由于在下载图片之前,我不知道图片的大小,所以我最初放了一个固定大小的UIImageView。下载图像后,我会调整UIImageView 的大小。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// Download image

    dispatch_async(dispatch_get_main_queue(), ^{

    // UIImageView resizing  
    });  
});

这一切都发生在cellForRowAtIndexPath
我在这里面临的问题是:
1.如何更新单元格的高度?考虑到单个单元格中可以有许多图像。所以我需要在上面下载时更改底部图像的位置。
2. 我尝试使用UITableView beginUpdatesendUpdates,但滚动到单元格顶部会导致用户体验不佳。

这就是 UI 在 reloadData 上的样子。有5张图片要下载:UI experience after UITableView reloadData

【问题讨论】:

  • beginUpdatesendUpdates 在这种情况下实际上无效 您不应该在组内调用 reloadData 文档说。
  • @HarikrishnanT:是的。提供糟糕的 UI 体验。
  • 我曾经遇到过同样的问题。问题是,如果你只重新加载一行,它正下方的单元格将隐藏在它后面,因为它的框架不会改变。然后我用 reloadData 把它卖掉了,它工作得很顺利。不确定您的用户体验问题是什么。
  • @HarikrishnanT :请查看我在链接上上传的视频。它显示了 reloadData 上的 UI 体验。
  • 为什么不使用reloadRowsAtIndexPaths:withRowAnimation:

标签: ios uitableview uiimageview grand-central-dispatch


【解决方案1】:

简答

  • estimatedRowHeight足够的喘息空间
  • 一旦 dequeueReusableCellWithIdentifier 返回时更改 UITableViewCell适用于缓存单元格
  • 使用reloadRowsAtIndexPaths 触发单个单元格重新加载
  • 使用 Core Data 管理您的缓存,并让 NSFetchedResultsController 样板代码完成所有 UI 工作。

详细介绍

没有意外滚动,只更新图像:

  1. 如果正在刷新的单元格是低于地平线,UITableView滚动
  2. 如果正在刷新的单元格高于顶部,UITableView滚动
  3. UITableView 只会在单元格清晰可见时滚动,并且需要比可用空间更多的空间。

UITableViewAutomaticDimension努力工作

您需要告诉 Cocoa Touch 该单元格是陈旧的,以便它会触发一个 dequeueReusableCellWithIdentifier,您将向其返回一个单元格具有适当的高度。
如果不重新加载整个表格视图或其部分,并假设您的索引是稳定的,请调用 -tableView:reloadRows:at:with: 并传递刚刚更改的单元格的 indexPath 和 .fade 动画。

代码:

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.estimatedRowHeight = 250 // match your tallest cell
    tableView.rowHeight = UITableViewAutomaticDimension
}

使用URLSession。当图像可用时,触发reloadRows:at:with

func loadImage(_ url: URL, indexPath: IndexPath) {
    let downloadTask:URLSessionDownloadTask =
        URLSession.shared.downloadTask(with: url, completionHandler: {
        (location: URL?, response: URLResponse?, error: Error?) -> Void in
        if let location = location {
            if let data:Data = try? Data(contentsOf: location) {
                if let image:UIImage = UIImage(data: data) {
                    self.cachedImages[indexPath.row] = image // Save into the cache
                    DispatchQueue.main.async(execute: { () -> Void in
                        self.tableView.beginUpdates()
                        self.tableView.reloadRows(
                            at: [indexPath],
                            with: .fade)
                        self.tableView.endUpdates()
                    })
                }
            }
        }
    })
    downloadTask.resume()
}

一旦进入缓存,cellForRow 只会从 UI 线程中读取:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let cell = tableView.dequeueReusableCell(withIdentifier: "id") as! CustomCell
    cell.imageView.image = cachedImages[indexPath.row]      // Read from the cache
    return cell
}

示例:从 *Wikipedia* 中获取一组随机图像

► 在GitHub 上查找此解决方案,在Swift Recipes 上查找更多详细信息。

【讨论】:

  • 在这种情况下我需要调用 heightForRowAtIndex 吗?
  • 没有。您将在链接的 Xcode 项目中看到,不需要进行任何遗留高度计算。 AutoLayout 完成了所有的工作。太神奇了。
  • 如果我是对的,表格单元格的代码中还没有实现自动布局。这是否表明,我需要使用自动布局创建单元格子视图才能工作?或者这不是必需的。
  • 示例代码加一。它可以按我的意愿工作。但想澄清上述评论
  • 使用旧方法,包括 heightForRowAtIndexPath 将在您提供新单元格时被调用。但是虚假滚动是由表格视图在您交换单元格时不知道在哪里着陆引起的。我只能说estimatedRowHeight + UITableViewAutomaticDimension 组合来防止这种闪烁。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-22
  • 2017-09-01
  • 2019-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多