目的
GeometryReaderで囲ったViewのHeightが固定値であれば、そのViewのHeightと同じ値をGeometryReaderのHeightに設定することで解決できる。
GeometryReaderで囲ったViewのHeightが可変である時、Viewの高さが決まってからGeometryReaderのHeightに反映させる必要がある。
その方法を自分用にメモしておきます。
問題のコード
struct ContentView: View {
var body: some View {
ScrollView {
blueView
redView
}
}
private var blueView: some View {
GeometryReader { geometry in
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: geometry.size.width / 2)
.foregroundStyle(.blue)
}
}
private var redView: some View {
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: 100)
.foregroundStyle(.red)
}
}
解決したコード
struct ContentView: View {
// ↓↓ ①追加
@State var blueViewHeight: CGFloat = 0
var body: some View {
ScrollView {
blueView
redView
}
}
private var blueView: some View {
GeometryReader { geometry in
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: geometry.size.width / 2)
.foregroundStyle(.blue)
// ↓↓ ④追加
.background {
GeometryReader { blueViewGeometry in
Color.clear
.preference(
key: BlueViewHeightKey.self,
value: blueViewGeometry.size.height
)
}
}
}
// ↓↓ ②追加
.frame(height: blueViewHeight)
// ↓↓ ⑤追加
.onPreferenceChange(BlueViewHeightKey.self) { height in
blueViewHeight = height
}
}
private var redView: some View {
Rectangle()
.frame(maxWidth: .infinity)
.frame(height: 100)
.foregroundStyle(.red)
}
}
// ↓↓ ③追加
struct BlueViewHeightKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {}
}
説明
① 確定した青いViewのHeightを格納するための変数です。
② 確定した青いViweのHeightをGeometryReaderのHeightに反映させています。
③ このKeyで確定した青いViewのHeightを流して、同じKeyで取得するために用意します。
④ backgroundでGeometryReaderを使うことで、青いViewのgeometryが取得できます。
このgeometryから確定した青いViewのHeightが取得できます。
Color.clearは③で作成したKeyを監視しているところに値を流すためのpreferenceをつけるためのものです。
そのため表示に影響しないColor.clearを使用しています。
⑤ ③で作成したKeyで流れてきた値を監視して①の変数に反映します。
※あくまで個人的な理解ですので間違えていたらすみません。
もっと推奨されるやり方があったらご教授頂けると助かります!

