SwiftUI ScrollView VStackGeometryReaderの高さは無視されます
VStackの外部でScrollViewを使用して、VStackが画面サイズを超えて拡張した場合にコンテンツをスクロールできるようにしたい。GeometryReaderVStack内で使用したいのですが、問題が発生します。これは、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をそのコンテンツと同じ大きさに拡大したいと思います。どうすればこれを達成できますか?
ありがとう
回答
これはGeometryReader魔法のツールではなく、現在のコンテキストの親で使用可能なスペースを読み取るだけですScrollViewが、内部コンテンツから必要なスペースを決定するため、独自の使用可能なスペースがなく、ゼロであることが理解されGeometryReaderます。サイクルがあります-子は親にサイズを要求しますが、親は子にサイズを期待します... SwiftUIレンダラーは、クラッシュしないように、これを何らかの方法で解決します(最小の既知のサイズを見つけます)。
これがあなたのシナリオのための可能な解決策です-ここでの適切な手段はビュー設定です。Xcode 12 / iOS14で準備およびテスト済み。
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を使用しないでください。