如何在图像下载和高度约束迅速改变后更新表格视图单元格高度?

Posted

技术标签:

【中文标题】如何在图像下载和高度约束迅速改变后更新表格视图单元格高度?【英文标题】:How to update tableview cell height after image downloaded and height constraint changed swift? 【发布时间】:2018-09-13 09:58:12 【问题描述】:

更新异步下载的图片高度约束后如何更新tableview单元格高度?

图片下载和约束改变后如何触发tableView单元重新布局?

最好的方法是什么?

已经尝试将代码放入 Dispatch 主队列,但结果同样糟糕。我在 cellForRow 方法中执行此操作,也将其移至 willDisplayCell。一次又一次的出现这个问题……

使用 Kingfisher 库进行图像缓存的代码示例:

    if let imgLink = post.imageLink 
                if let url = URL(string: imgLink) 

                    cell.postImage.kf.setImage(with: url, placeholder: UIImage(), options: nil, progressBlock: nil)  (image, error, cacheType, imageURL) in

                        if let image = image, cell.tag == indexPath.row 
                            cell.heightConstraint.constant = image.size.height * cell.frame.size.width / image.size.width   
                        
                    
                
    

【问题讨论】:

如果你设置了带有自动布局的单元格,并且锚点指定了单元格的高度而没有歧义,那么你只需要在图片加载后在 tableView 上调用 beginUpdates 和 endUpdates。 好吧,imageView 对单元格的 contentView 有顶部、底部、前导和尾部约束,还有一个高度约束,可以根据从服务器下载的图像比例进行更改。我在下载方法的完成处理程序中调用了开始/结束更新,但存在滚动和跳转问题。 如果你有代码,你应该分享它。 你是否尝试在加载图像后在主线程中更新表格。 是的,同样奇怪的跳跃滚动和随机单元格重新加载。 【参考方案1】:

您可以尝试调用这 2 行以在图像可用后重新计算单元高度:

tableView.beginUpdates()
tableView.endUpdates()

请参阅文档https://developer.apple.com/documentation/uikit/uitableview/1614908-beginupdates:“您还可以使用此方法和 endUpdates() 方法来动画行高的变化,而无需重新加载单元格。”

【讨论】:

【参考方案2】:

恕我直言,作为ppalancica pointed out,调用 beginUpdates 和 endUpdates 是理想的方式。您不能从 UITableViewCell 内部引用 tableView,正确的方法是使用委托并从实现委托的 ViewController 调用 beginUpdates 和 endUpdates。

代表:

protocol ImageCellDelegate 
    func onLayoutChangeNeeded()

UITableViewCell 实现:

class ImageCell: UITableViewCell 
    var imageView: UIImageView = ...
    var delegate: ImageCellDelegate?
    ...

    func setImage(image: UIImage) 
        imageView.image = image
        //calling delegate implemented in 'ViewController'
        delegate?.onLayoutChangeNeeded()
    

    ...

ViewController 实现:

class ViewController: UIViewController, UITableViewDataSource, ImageCellDelegate 
    var tableView: UITableView = ...
    .....
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let imageCell = tableView.dequeueReusableCell(withIdentifier: id, for: indexPath) as! ImageCell
        //setting 'delegate' here
        imageCell.delegate = self
        return imageCell
    
    
    //called from 'ImageCell' when 'image' is set inside 'setImage'
    func onLayoutChangeNeeded() 
        tableView.beginUpdates()
        tableView.endUpdates()
    

    .....

【讨论】:

【参考方案3】:

我遇到了同样的问题。您需要记住的是 Tableview 重用单元格并且您正在异步加载图像。

推荐:您可以要求您的反手团队为您提供图像的高度和宽度,以便您计算单元格高度并尽快返回。

如果您不能这样做,您可以在数据源中保留已下载图像的大小。因此,在您下载图像之前,请检查您的数据源的图像大小并更新高度约束常量。

另一件事是你应该在 cellForRow 和 willDisplay 单元格中都这样做(我知道这不是好的做法,但要满足 tableview 自动尺寸)

更新高度常数后,您应该使用这对代码重新加载您的单元格。

    cell.setNeedsLayout()
    cell.layoutIfNeeded()

我是怎么做到的

 if let imagesize = post.imageSize  
      cell.updateHeightPerRatio(with: imagesize)

  else 
      // Here load size from URL  and update your datasource 

   
  // Load image from URL with any SDWebimage or any other library you used

【讨论】:

尝试这种方法,但我的单元格直到滚动离开屏幕并返回屏幕后才会调整大小。有什么建议吗? @LukeIrvin 我面临同样的问题。你解决了吗?【参考方案4】:

我实际上做了以下工作:

if (self.firstLoaded[indexPath.row] == false) 
                            self.firstLoaded[indexPath.row] = true
                            DispatchQueue.main.async 
                                self.tableViewController.tableView.reloadData()
                            

firstLoaded 只是告诉表格该行已经从 URL 接收到图像并计算/存储了正确的高度。

另外,我用过这个:

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
        cellHeights[indexPath] = cell.frame.size.height


override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        return UITableViewAutomaticDimension


override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat 
        if let height = cellHeights[indexPath] 
            return height
        
        return 1500

我知道调用 reloadData() 不是一个好习惯,但它解决了我的问题。如果有人有其他建议,请随时分享。

谢谢!

【讨论】:

以上是关于如何在图像下载和高度约束迅速改变后更新表格视图单元格高度?的主要内容,如果未能解决你的问题,请参考以下文章

更新图像高度约束后更新表格视图单元格高度

重用 tableviewcell 中的约束和高度未更新

图像在表格视图单元格中下载后调整大小

如何根据标签文本快速更改表格视图单元格高度?

我可以设置我的自动布局约束,以便将图像视图设置为图像的高度

iOS - 表格视图单元格中的自动布局问题