[SwiftUI] GeometryReaderでメソッド/関数を実行したい!
実行環境
- Xcode 12.5
GeometryReaderでViewのサイズを取得する
[SwiftUI] GeometryReaderでViewのサイズを知るで解説したとおり、SwiftUIでViewのサイズを取得するにはGeometryReaderを使います。
GeometryReader
のハンドラーから渡されるGeometryProxy
のインスタンスでViewのサイズがわかります。
struct LocalVariableView: View {
var body: some View {
GeometryReader { proxy in
Text("\(proxy.frame(in: .local).debugDescription)")
}
}
}
struct LocalVariableView_Previews: PreviewProvider {
static var previews: some View {
LocalVariableView()
.previewLayout(.fixed(width: 200, height: 100))
}
}
実行結果はこの通り。
上記のコードではText
ViewにViewの座標を表示していましたが、ローカル変数として計算してメソッドを実行したい場合があるでしょう。
しかしGeometryReader
のハンドラー内は@ViewBuilder
の記法が適応されているので、一文ずつViewプロトコルに適合したインスタンスを返す必要があります。
これではVoidを返す関数やメソッドが実行できません。
試しにprint
関数を実行してみましょう。
var body: some View {
GeometryReader { proxy in
let frame = proxy.frame(in: .local).debugDescription
print(frame) // Voidを返す関数
Text("\(proxy.frame(in: .local).debugDescription)")
}
}
するとType '()' cannot conform to 'View'
というコンパイルエラーが表示されます。
このエラーを避けるため、GeometryReader
のハンドラーに明確に返すViewの型を指定する方法があります。
GeometryReader { proxy -> Text in
let frame = proxy.frame(in: .local).debugDescription
print(frame)
return Text("\(proxy.frame(in: .local).debugDescription)")
}
proxy -> Text
とすることで返す型を指定してreturn
でText
を返します。
するとコンパイルが通ります。
Viewサイズ取得してVoid関数を実行する
ここからは私が実際体験した話ですが、Image Viewのサイズを取得したい場合がありました。
取得したサイズは計算のみに使うので特定のViewを表示する必要はありません。
.overlay
修飾子とEmptyView
を使って以下のようなコードを実装しました。
Image("people")
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(
GeometryReader { proxy -> EmptyView in
let frame = proxy.frame(in: .local).debugDescription
print(frame)
return EmptyView()
}
)
ところが、コードを実行してもprint(frame)
が呼ばれませんでした。
完全に想像ですが、EmptyView
はViewが描写されないのでSwiftUIの方で実行しないように最適化しているのでしょうか?
AnyView
を返すようにするとprint
関数が呼ばれました。
Image("people")
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(
GeometryReader { geo -> AnyView in
print(geo.frame(in: .local).debugDescription)
return AnyView(EmptyView())
}
)
まとめ
GeometryReader
のハンドラー内でメソッドや関数を実行したい場合は
- @ViewBuilderの記法から抜け出すために、ハンドラーに明確に返す型を指定して、returnでViewを返す
- Viewを表示しないからといって
EmptyView
を返すとそもそも実行されないことがある。 AnyView
で返す
宣伝
インプレスR&D社より、「1人でアプリを作る人を支えるSwiftUI開発レシピ」発売中です。
「SwiftUIでアプリを作る!」をコンセプトにSwiftUI自体の解説とそれを組み合わせた豊富なサンプルアプリでどんな風にアプリ実装すればいいかが理解できる本となっています。
iOS 14対応、Widgetの作成も一章まるまるハンズオンで解説しています。
SwiftUIを学びたい方、ぜひこちらのリンクをチェックしてください!