【问题标题】:SwiftUI ScrollView VStack GeometryReader height ignoredSwiftUI ScrollView VStack GeometryReader 高度被忽略
【发布时间】:2020-10-10 12:29:58
【问题描述】:

我想在 VStack 之外使用 ScrollView,以便在 VStack 扩展超出屏幕大小时我的内容是可滚动的。 现在我想在 VStack 中使用GeometryReader,它会导致问题,我只能通过设置 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)
  }
}

这会产生以下图像:

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

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

谢谢

【问题讨论】:

    标签: swiftui geometryreader


    【解决方案1】:

    应该理解GeometryReader不是什么神器,它只是读取当前上下文parent中的可用空间,但是……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()
        }
    }
    

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

    【讨论】:

    • 谢谢!偏好值设置在哪里?如果我将初始值设置为 0,我的视图高度为 0。如果我将其设置为 10,则为 10。
    • 查看添加的更多 cmets,查看首选项在同一渲染周期中将值从子级传递到父级。
    • 谢谢!奇怪的是它对我不起作用。似乎没有调用 onPreferenceChanged 。我会努力的。