基于动态集合视图的 UITableView 的动态高度

Posted

技术标签:

【中文标题】基于动态集合视图的 UITableView 的动态高度【英文标题】:Dynamic height for a UITableView based on a dynamic collection view 【发布时间】:2019-01-04 11:45:57 【问题描述】:

我必须在UITableViewCell 中添加一个UICollectionViewcollectionView 可以有不同数量的项目。 所以collectionView 应该在tableView 内正确调整。

我已经在我的项目中实现了这个: https://github.com/vishalwaka/DynamicCollectionViewInsideTableViewCell

在我的情况下,在设置collectionView 的高度后,单元格的高度没有被调整。

如何将collectionView 设置为不可滚动并显示tableviewcell 中的所有内容。

【问题讨论】:

How to dynamically change the height of a UITableView Cell containing a UICollectionView cell in Swift?的可能重复 @Sateesh 我必须使用不同宽度的集合视图单元格,所以我无法计算会有多少行。 请分享一些示例代码。 @Sateesh 在共享的 GitHub 项目中。 你试过this吗? 【参考方案1】:

UITableViewCell 内创建一个collectionView,具有如下的Leading、Trailing、Top、Bottom、Height 约束:

现在,在您的 tableViewCell 类中创建一个 NSKeyValueObserver 并将其添加到 UICollectionViewcontentSize 属性并将 contentSize 更改分配给 collectionView 的高度约束。

class FormCollectionTableViewCell: UITableViewCell

  var collectionViewObserver: NSKeyValueObservation?

  override func awakeFromNib() 
    super.awakeFromNib()
    addObserver()
  

  override func layoutSubviews() 
        super.layoutSubviews()
        layoutIfNeeded()
  

  func addObserver() 
       collectionViewObserver = collectionView.observe(\.contentSize, changeHandler:  [weak self] (collectionView, change) in
            self?.collectionView.invalidateIntrinsicContentSize()
            self?.collectionViewHrightConstarint.constant = collectionView.contentSize.height
            self?.layoutIfNeeded()
        )
    
   deinit 
      collectionViewObserver = nil
   

并在您的 viewController 类中使用以下代码:

override func viewDidLayoutSubviews() 
        super.viewDidLayoutSubviews()
        tableView.reloadData()

现在,您的表格视图将具有动态高度的集合视图。不要忘记禁用UICollectionView 的滚动并调整它的流量。

【讨论】:

我在提前指定高度时遇到了约束中断。在将约束更改为低优先级后,集合视图的高度不合适。你能分享你的项目吗? collectionView.contentSize.height 正在正确设置高度,但之后不会扩大或缩小单元格高度。 @vishalwaka:您是否将高度约束的优先级设置为 1000 并在 viewController 类中实现 viewDidLayoutSubviews。对我来说它工作正常。 所有layoutIfNeeded 都不是真正必要的,也不应该被需要。 @nikksindia 对于 1000 的高度约束,约束将开始中断。我已经在我的 viewController 类中实现了 viewDidLayoutSubviews。我已经更新了github.com/vishalwaka/DynamicCollectionViewInsideTableViewCell 的代码。有时也有视图没有正确更新。【参考方案2】:

创建一个collectionView的以下子类:

class IntrinsicSizeCollectionView: UICollectionView 

required init?(coder aDecoder: NSCoder) 
    super.init(coder: aDecoder)
    self.setup()


override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) 
    super.init(frame: frame, collectionViewLayout: layout)

    self.setup()


override func layoutSubviews() 
    super.layoutSubviews()
    if !self.bounds.size.equalTo(self.intrinsicContentSize) 
        self.invalidateIntrinsicContentSize()
    


override var intrinsicContentSize: CGSize 
    get 
        let intrinsicContentSize = self.contentSize
        return intrinsicContentSize
    


func setup() 
    self.isScrollEnabled = false
    self.bounces = false

将 collectionView 添加到 tableViewCell 并将其约束设置前导、尾随、顶部、底部到单元格的边界。还要像屏幕截图中那样设置内在大小。

tableViewCell 的大小应设置为 UITableViewAutomaticdimension,并且此单元格的估计高度应在 TableView 的 dataSource 中设置。

应该就是这样了。

【讨论】:

即使在进行了这些更改之后,我也遇到了同样的问题。有时,当集合视图的内容发生变化时,单元格不会展开。此外,收缩没有正常进行。 你能分享你的项目吗?【参考方案3】:

在检查您的项目后,我建议您使用稍微不同的方法。

更新模型后,您可以向集合视图布局询问新的内容大小(collectionViewLayout.collectionViewContentSize)。然后更新高度约束常量。

所以,你应该:

    更新您的模型 重新加载行 询问布局以获取其新的contentSize 更新高度约束常量

你的项目中的这个修改应该实现这个:

// Remove the content size observing logic.

func configureForVehicle(_ vehicle: Vehicle, selectedHouseType: String) 
    self.vehicle = vehicle

    applianceCollectionView.reloadData()

    // Set the height constraint to the new value, found in the layout's content size info.
    // Since the model (self.vehicle) was already updated, the layout will return the new content size.
    // Because this is all happening while the cell is being reloaded, the cell will also resize properly.
    // You don't need to perform any other setNeedsLayout/layoutIfNeeded calls.
    self.applianceCollectionViewHeightConstraint.constant = applianceCollectionView.collectionViewLayout.collectionViewContentSize.height

请注意关于不需要任何额外布局更新调用的注释。只打电话 tableView.reloadRows(at: [indexPath], with: .automatic) 应该足够了。不必要的布局更新可能会导致性能下降。


附带说明,如果您希望在不重新加载单元格的情况下为高度变化设置动画,可以使用另一种方法。在这种情况下,您需要保留/查找对要更新的单元格的引用,然后执行以下操作:

    在不调用任何表格视图重新加载调用的情况下更新单元格的模型。 在更新的单元格上重新加载集合视图。 更新高度约束的方式与上述相同。 使用某种回调通知您的表格视图单元格希望更新其高度。 在回调调用tableView.beginUpdates(); tableView.endUpdates()。这会导致 tableView 重新布局其单元格并动画高度变化。

来自beginUpdates() documentation:

您还可以使用此方法后跟 endUpdates() 方法来动画行高的变化,而无需重新加载单元格。这组方法必须以调用 endUpdates() 结束。

【讨论】:

您建议的解决方案对我不起作用。您可以通过更新您机器上的项目来检查一次吗? 如果我执行以下操作,您的项目对我有效: 1. 从 ViewControllerlayoutSubviews() 中删除 viewDidLayoutSubviews() 覆盖 ApplianceTableViewCell 2. 删除 @987654331 中的内容大小观察器@ 3. 实现我在ApplianceTableViewCell中回答的代码 编辑:再次检查后,我注意到applianceCollectionView.reloadData() 毕竟是必要的。编辑了我的答案。

以上是关于基于动态集合视图的 UITableView 的动态高度的主要内容,如果未能解决你的问题,请参考以下文章

设置表格视图单元格的动态高度

如何为 UITableView 部分标题动态设置背景颜色?

集合视图组合布局 - 动态布局

显示collectionView的标头大小,基于该标头内部的集合视图动态内容不起作用

动态改变 UITableView 高度

实现 UITableView (Slave) 的动态高度,它作为子视图添加到另一个具有动态高度的 UITableView (Master) 的自定义单元格中