SwiftUI:将List放入ScrollView(获取List的内容高度)

SwiftUI: Put List into ScrollView (get List's content height)

我正在尝试将 List 放入 ScrollView

如果你想知道为什么,这是我的解释,如果不是 - 直接跳到代码段。我需要在我的 List 之上平滑缩放 header。为此,我应该将 header 和 List 都放在 ScrollView 上,然后使用滚动内容偏移来完成其余的工作。我还需要禁止 List 的 built-in 滚动,因为它已经在 ScrollView 上,并固定 List 的高度 - 所以滚动视图知道它的内容高度。效果很好,但 List 的高度有时(!)是错误的。

我不能使用 LazyVStack 而不是 List - 它更慢(我猜没有单元格重用),而且 List 很大。它还打破了 NavigationLinks,所以它出来了 - 它必须是 List.

单元格的高度不固定,无法手动计算

这是我使用 UITableView:

创建具有固定高度的不可滚动 List 的代码
struct AutosizingList<Content: View>: View {

    @ViewBuilder var content: Content
    @State private var tableContentHeight: CGFloat = 0

    var body: some View {
        List {
            content
        }
        .introspectTableView { tableView in
            tableView.isScrollEnabled = false
            tableContentHeight = tableView.contentSize.height
        }
        .frame(height: tableContentHeight)
    }
}

我想这个块并不总是在 didLayoutSubviews 之后调用,这就是为什么高度有时正确有时错误的原因。所以这是我的问题:如何可靠地获得 List 的身高?我可以在 SwiftUI 中使用类似于 didLayoutSubviews 的东西吗?

Table contentSize 是根据已经出现在屏幕上的单元格计算的。假设剩余单元格的大小与之前的单元格大小相同。

您可以看到滚动指示器在您滚动时如何变化:它可能会减少或增加,具体取决于新单元格的大小。在我的测试中,我使用依赖于索引的单元格高度来显示此行为。

反过来 introspectTableView 在创建视图时只调用一次:您可以检查 source code.

我建议你在这种情况下使用关键路径观察:

struct ContentView: View {
    @State
    private var observation: NSKeyValueObservation?

    var body: some View {
        List {
            ForEach(0..<100) { i in
                Text(String(i))
                    .frame(height: CGFloat(i))
            }
        }.introspectTableView { tableView in
            print("initial size", tableView.contentSize)
            observation = tableView.observe(\.contentSize) { tableView, value in
                print("new size", tableView.contentSize)
            }
        }
    }
}