SwiftUI ScrollView VStack GeometryReader 高度被忽略

Jan*_*ner 5 swiftui geometryreader

我想在 VStack 之外使用 ScrollView,这样当 VStack 扩展超出屏幕尺寸时我的内容就可以滚动。现在我想GeometryReader在 VStack 中使用,它会导致问题,我只能通过设置 GeometryReader 框架来解决,考虑到我使用阅读器来定义视图大小,这并没有真正帮助我。

这是没有 ScrollView 的代码,它工作得很好:

struct MyExampleView: View {
  var body: some View {
    VStack {
        Text("Top Label")
            .background(Color.red)
        
        GeometryReader { reader in
            Text("Custom Sized Label")
                .frame(width: reader.size.width, height: reader.size.width * 0.5)
                .background(Color.green)
        }
        
        Text("Bottom Label")
            .background(Color.blue)
    }
    .background(Color.yellow)
  }
}
Run Code Online (Sandbox Code Playgroud)

这会产生以下图像:

没有滚动视图

自定义尺寸的标签应为全宽,但高度为宽度的一半。现在,如果我将相同的代码包装在 ScrollView 中,就会发生这种情况:

带滚动视图

不仅所有东西都变小了,而且自定义尺寸标签的高度也被忽略了。如果我设置 GeometryReader 的高度,我可以调整该行为,但我希望 GeometryReader 的大小与其内容一样大。我怎样才能实现这个目标?

谢谢

Asp*_*eri 5

应该理解,这GeometryReader不是一个神奇的工具,它只是读取当前上下文父级中的可用空间,但是......ScrollView没有自己的可用空间,它为零,因为它从内部内容确定所需的空间......所以GeometryReader在这里使用你有一个循环 - 孩子向父母询问尺寸,但父母期望孩子的尺寸... SwiftUI 渲染器以某种方式解决了这个问题(找到最小的已知尺寸),只是为了不崩溃。

这是适合您的场景的可能解决方案 - 这里合适的工具是视图首选项。使用 Xcode 12 / iOS 14 进行准备和测试。

演示

struct DemoLayout_Previews: PreviewProvider {
    static var previews: some View {
        Group {
          MyExampleView()
          ScrollView { MyExampleView() }
        }
    }
}

struct MyExampleView: View {
    @State private var height = CGFloat.zero
    var body: some View {
        VStack {
            Text("Top Label")
                .background(Color.red)
            
            Text("Custom Sized Label")
                .frame(maxWidth: .infinity)
                .background(GeometryReader {
                    // store half of current width (which is screen-wide)
                    // in preference
                    Color.clear
                        .preference(key: ViewHeightKey.self, 
                            value: $0.frame(in: .local).size.width / 2.0)
                })
                .onPreferenceChange(ViewHeightKey.self) {
                    // read value from preference in state
                    self.height = $0
                }
                .frame(height: height) // apply from stored state
                .background(Color.green)
            
            Text("Bottom Label")
                .background(Color.blue)
        }
        .background(Color.yellow)
    }
}

struct ViewHeightKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:...如果您不确定视图所在的上下文,请不要使用 GeometryReader。