在 ScrollView 内更新子视图的高度(没有滚动的 UITableView)后 UIStackView 不更新

UIStackView not updating after updating height of subview (UITableView with no scroll) while inside a ScrollView

当 stackView 在滚动视图中时,如何使 UIStackView 重新分配它的子UITableViews

我的布局层次是基于苹果的官方文档Dynamic content for StackViews

 - UISCrollView
   - UIStackView
     - UIView A
     - UIView B
     - UIView C
     - UITableView X
     - UITableView Y
     - UIView D

约束已按文档设置。 StackView 的初始布局是正确的,显示了所有可见的子视图。当强制常规视图扩展到屏幕高度之外时,滚动会按预期工作。此外,在故事板中查看布局时,一切都按预期堆叠。
此时 UITableView 是空的。一旦我向 tableView 添加内容,问题就出现了。

问题
当我通过调用 .reloadData() 来动态更新 TableView 时,我看到它们的内容出现了。 (thanks to this answer 关于非滚动 tableView)但是 UIStackView 没有堆叠 UITableView。

我的猜测是我需要使 stackview 无效,或者以某种方式让它重新分配它的子视图。我该怎么做?

首先,警告:

您要实现的并不是真正标准的 iOS 行为。您应该首先考虑一种不同的方法,例如创建具有多个部分的单个分组 table 视图。您可以在 table 视图中将自定义视图实现为节页眉或页脚。


现在,如果您真的想采用原来的方法...

...出于某些重要原因,您应该知道 table 视图在默认情况下没有固有的内容大小。因此,您需要告诉 table 视图它应该有多高,否则它只会缩小到零高度。

您可以通过继承 UITableView 并覆盖其 intrinsicContentSize() 来实现此目的,正如 Rob 在 this answer 中对类似问题所建议的那样。

或者您为每个 table 视图添加高度限制并在代码中动态设置它们的常量。一个简单的例子:

  1. 将您的 table 视图添加到 Interface Builder 中的垂直堆栈视图。
  2. 为两个 table 视图提供前导和尾随约束,以将它们的左右边缘固定到堆栈视图。
  3. 在相应的视图控制器中为您的 table 视图创建出口:

    @IBOutlet weak var tableView1: UITableView!
    @IBOutlet weak var tableView2: UITableView!
    
    @IBOutlet weak var tableView1HeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var tableView2HeightConstraint: NSLayoutConstraint! 
    
  4. 覆盖该视图控制器的 updateViewConstraints() 方法:

    override func updateViewConstraints() {
        super.updateViewConstraints()
        tableView1HeightConstraint.constant = tableView1.contentSize.height
        tableView2HeightConstraint.constant = tableView2.contentSize.height
    }
    
  5. 现在,每当您的任何 table 视图的内容发生变化时(例如,当您添加或删除行或更改单元格内容时),您需要告诉您的视图控制器它需要更新其约束。假设您有一个按钮,每次点击它都会向 tableView1 添加一个单元格。您可以像这样实现它的操作:

    @IBAction func buttonTappen(sender: AnyObject) {
        // custom method you implement somewhere else in your view controller
        addRowToTableView1DataSource()
        // reload table view with the updated data source
        tableView1.reloadData()
        // triggers an updateViewConstraints() call
        view.setNeedsUpdateConstraints()
    }
    

tl;博士:

A UITableView 不适合在未启用滚动的情况下使用,因此您始终需要在其内容更改时明确设置其高度 - 可能是使用约束或通过覆盖 table 视图内在内容大小。