使用 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 吗?

iOS 开发实践之Auto Layout(From Vincent Sit)

WWDC2018 之 高性能 Auto Layout