基于动态集合视图的 UITableView 的动态高度
Posted
技术标签:
【中文标题】基于动态集合视图的 UITableView 的动态高度【英文标题】:Dynamic height for a UITableView based on a dynamic collection view 【发布时间】:2019-01-04 11:45:57 【问题描述】:我必须在UITableViewCell
中添加一个UICollectionView
。
collectionView
可以有不同数量的项目。
所以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
并将其添加到 UICollectionView
的 contentSize 属性并将 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. 从ViewController
和 layoutSubviews()
中删除 viewDidLayoutSubviews()
覆盖 ApplianceTableViewCell
2. 删除 @987654331 中的内容大小观察器@ 3. 实现我在ApplianceTableViewCell
中回答的代码
编辑:再次检查后,我注意到applianceCollectionView.reloadData()
毕竟是必要的。编辑了我的答案。以上是关于基于动态集合视图的 UITableView 的动态高度的主要内容,如果未能解决你的问题,请参考以下文章
显示collectionView的标头大小,基于该标头内部的集合视图动态内容不起作用
实现 UITableView (Slave) 的动态高度,它作为子视图添加到另一个具有动态高度的 UITableView (Master) 的自定义单元格中