使用 Auto Layout 将 UITableView 的高度设置为其内容的高度
Posted
技术标签:
【中文标题】使用 Auto Layout 将 UITableView 的高度设置为其内容的高度【英文标题】:Set UITableView's height to the height of its content with Auto Layout 【发布时间】:2016-01-20 23:46:09 【问题描述】:我有一个视图,里面有两个标签和一个表格视图。我希望标签 1 始终位于我的表格视图上方,而标签 2 位于表格视图下方。问题是表格视图需要自动调整大小,这意味着增加或减少高度。
现在我有一个约束说 Table View 的高度总是等于 85 和一个 @IBOutlet
到我可以更改常量的高度约束。
我猜我需要将常数更改为所有单元格的高度,但我不确定如何。
【问题讨论】:
另一种解决方案是创建UITableView
的子类,将其自己的intrinsicContentSize.height
设置为其contentSize.height
。见***.com/a/17335818/77567。
@robmayoff 答案在我看来更好,因为它比 joern 答案更有活力。
我知道这不是您问题的答案,但您是否考虑过使用 UIStackview 而不是 tableview?
【参考方案1】:
您必须在 UIViewController 中覆盖 updateViewConstraints()
并将高度约束的常量设置为 tableView.contentSize.height:
override func updateViewConstraints()
tableHeightConstraint.constant = tableView.contentSize.height
super.updateViewConstraints()
然后你必须确保Label2
有一个顶部约束,即greaterThanOrEqual
到表视图的底部。而且您还必须将表格视图的高度约束的优先级从Required
更改为High
,以避免在表格视图的contentHeight
大于可用高度时发生冲突。
【讨论】:
在 viewDidLayoutSubviews 中添加这个,对我有用。 @GMHSJ,afaik viewDidLayoutSubviews 是在布局系统计算完帧后调用的,因此如果你在这里更改约束,它会再次触发布局过程,最终会产生无限递归。 @vahotm:没错,但是 updateViewConstraints 对我不起作用。不知道为什么:( 虽然我认为这是一个很好的解决方案,但它需要您根据约束调整 tableview 的高度。我发现 nova 下面的解决方案更好,因为它不依赖于使用约束来改变高度。在我的例子中,我的 tableView 是一个 subview,superview 需要根据 tableView 的大小调整自己的框架。 @GMHSJ,在updateViewConstraints
末尾调用super.updateViewConstraints()
方法有帮助吗?因为这是根据the documentation覆盖它的正确方法。【参考方案2】:
还有另一种方法可以做到这一点。
您可以在 table view contentSize
变量上添加观察者,并在 table view 内容更改时添加自己的大小。
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var tableHeightConstraint: NSLayoutConstraint!
override func viewDidLoad()
super.viewDidLoad()
self.tableView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.new, context: nil)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
tableView.layer.removeAllAnimations()
tableHeightConstraint.constant = tableView.contentSize.height
UIView.animate(withDuration: 0.5)
self.updateViewConstraints()
【讨论】:
它对我有用。但是“observeValueForKeyPath”方法被多次调用。 @iKT 当您使用 NSKeyValueObservingOptions.new 观察键时,每次该键更新为新值时都会调用您,因此您可以随时删除该观察者 这救了我的命 太棒了,拯救我的一天 有用。这不是一种优雅的方式。实际上很多年,APPLE 并没有通过事件 didFinishLoad 实现一种时尚的方式来做到这一点,如“tableview.contentSize”。但到目前为止,这是一种更简洁的方法,无需创建特定的类或扩展来处理此类普通任务。【参考方案3】:下面的代码对我有用。
为 tableViewHeight 创建一个插座。
@IBOutlet weak var tableViewHeight: NSLayoutConstraint!
var tableViewHeight: CGFloat = 0 // Dynamically calcualted height of TableView.
对于单元格的动态高度,我使用了以下代码:
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
return tableView.estimatedRowHeight
对于 TableView 的高度,以及 TableView 单元格的动态高度:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
print(cell.frame.size.height, self.tableViewHeight)
self.tableViewHeight += cell.frame.size.height
tableViewBillsHeight.constant = self.tableViewHeight
说明:
TableView 单元格创建后,我们获取即将显示的单元格的框架高度,并将该单元格的高度添加到主 TableView 中。
【讨论】:
【参考方案4】:在 Swift 4.2 和 Xcode 10.1 中
这里有两个选项可以根据内容动态设置 UITableView 的高度。
1) 如果您获得内容大小,请在viewDidLayoutSubviews()
中使用此代码
DispatchQueue.main.async
var frame = self.TblView.frame
frame.size.height = self.TblView.contentSize.height
self.TblView.frame = frame
2) 基于表视图高度约束更新表视图高度。在重新加载表视图时得到服务器的响应后编写此代码。
DispatchQueue.main.async
self.TblViewHeightConstraint.constant = CGFloat((self.array.count) * 30)//Here 30 is my cell height
self.TblView.reloadData()
【讨论】:
【参考方案5】:将@nova 答案扩展到 swift 4
tableViewSizeObservation = tableView.observe(\.contentSize) [weak self] (tableView, _) in
tableView.layer.removeAllAnimations()
self?.tableViewHeightContraint.constant = tableView.contentSize.height
UIView.animate(withDuration: 0.5)
self?.view.updateConstraints()
self?.view.layoutIfNeeded()
deinit
tableViewSizeObservation?.invalidate()
【讨论】:
【参考方案6】:对于 TableView 高度自动调整及其内容大小,请在 ViewController 类中使用它。
//constraintTableViewHeight :- tableview height constraint
override func viewWillLayoutSubviews()
super.updateViewConstraints()
self.constraintTableViewHeight.constant = self.tableView.contentSize.height
并添加这个
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
self.viewWillLayoutSubviews()
这段代码很容易根据内容大小自动调整tableview的高度。
【讨论】:
【参考方案7】:一种更现代的方式是这样做:
override func updateViewConstraints()
tableView.heightAnchor.constraint(equalToConstant: tableView.contentSize.height).isActive = true
super.updateViewConstraints()
【讨论】:
但是每次调用updateViewConstraints
时,都会创建一个新的约束。因此,您正在累积多个高度约束,因此如果 tableView.contentSize 发生变化,您将获得具有冲突值的高度约束。因此,存储高度约束并始终更新该约束是值得的(您可以在 viewDidLoad 中创建它)。此外,即使尺寸永远不会改变,自动布局也必须做更多的工作来处理额外的约束。【参考方案8】:
override func viewDidLayoutSubviews()
super.viewDidLayoutSubviews()
let contentheight = tableView.contentSize.height
self.tableHeightConstraint.constant = contentheight
【讨论】:
以上是关于使用 Auto Layout 将 UITableView 的高度设置为其内容的高度的主要内容,如果未能解决你的问题,请参考以下文章
UIStackView 和 Auto Layout Anchor 的区别
iOS 8 Storyboard Auto Layout 中的 UIImageView 拉伸
使用Auto Layout中的VFL(Visual format language)--代码实现自动布局转
可以用 Auto Layout 代替 Asset Catalog 吗?