我想在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可以增长到与其内容一样大。我该如何实现?
谢谢
答案 0 :(得分:1)
应该理解,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()
}
}
注意:...如果您不确定视图所在的上下文,请不要使用GeometryReader。