【Swift UI】GeometryReaderの使い方!ビューの幅や高さ、座標を取得する
この記事からわかること
- Swift UIのGeometryReaderの使い方
- ビューの幅や高さ、座標の取得方法
- GeometryProxy構造体とは?
- 列挙型CoordinateSpaceとは?
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
GeometryReaderとは?
@frozen
struct GeometryReader<Content> where Content : View
Swift UIのGeometryReaderはコンテンツのサイズや位置に関する情報を取得するためのコンテナビューです。
使い方
init(@ViewBuilder content: @escaping (GeometryProxy) -> Content)
GeometryReader
を使用するにはビューを囲うように実装します。イニシャライザの通りにcompletionHandlerになっており(GeometryProxy) -> Content
型のクロージャーを参照できます。一度使用例を見てみます。
おすすめ記事:【Swift】completionHandlerとは?使い方と@escapingの意味
var body: some View {
GeometryReader { geometry in
Text("Width: \(geometry.size.width)\nHeight: \(geometry.size.height)")
.offset(y:geometry.size.height)
}.frame(width: 200, height: 200)
.background(Color.cyan)
}
GeometryReader
でビュー(GeometryReader自体)の横幅と高さを取得しています。中のText
の大きさではなくGeometryReaderとして確保している領域のサイズや座標を取得できる感じです。
GeometryProxy構造体
struct GeometryProxy
GeometryProxy
構造体はコンテナビューのサイズと座標空間にアクセスするためのプロキシ(代理)オブジェクトです。このプロキシを介して実際の値を参照していきます。
ビューの横幅と高さを取得する
var size: CGSize { get }
GeometryProxy
構造体のプロパティにCGSize
型のsize
プロパティを保持しており、そのwidth
プロパティから横幅をheight
プロパテから高さを取得することができます。
geometry.size.width
geometry.size.height
CGSize
や後述するCGRect
については以下の記事を参考にしてください。
ビューの座標を取得する
func frame(in coordinateSpace: CoordinateSpace) -> CGRect
GeometryProxy
構造体のメソッドにはframe(in:)メソッド
が定義されており返り値でCGRect
型を取得することができます。取得したCGRect
型からビューのx座標の最大値やy座標の最小値などを取得することが可能です。
geometry.frame(in: .global).maxY
struct CGRect {
var height: CGFloat // 長方形の高さ
var width: CGFloat // 長方形の幅
var minX: CGFloat // x座標の最小値
var midX: CGFloat // 中間部分のx座標
var maxX: CGFloat // x座標の最大値
var minY: CGFloat // y座標の最小値
var midY: CGFloat // 中間部分のy座標
var maxY: CGFloat // y座標の最大値
}
列挙型:CoordinateSpace
frame(in:)メソッド
の引数にはCoordinateSpace
型で軸とする座標空間を指定します。
enum CoordinateSpace {
case global // ビュー階層のルートにあるグローバル座標空間
case local // 現在のビューのローカル座標空間
case named(AnyHashable) // ビューのローカル座標空間への名前付き参照
}
global
とlocal
の違いはその名の通り基準とする座標の違いになります。globalであれば画面の左上を(0.0)とし、localであればビューの左上を(0.0)とします。
GeometryReader { geometry in
Text("Global: \(geometry.frame(in: .global).maxY)\nLocal: \(geometry.frame(in: .local).maxY)")
.offset(y:geometry.size.height)
}.frame(width: 200, height: 200)
.background(Color.cyan)
大きさを指定しないと重なってしまう
GeometryReader
では内部に設置したビューに対して明示的にサイズや位置を指定しないとビューが重なってしまいます。
GeometryReader { geometry in
Text("Width:\(geometry.size.width)")
.background(Color.cyan)
Text("Height:\(geometry.size.height)")
}
まずGeometryReader
は他に要素がなく、自身の大きさが指定されていない場合は一番左上の(0.0)を基準に全体に広がります。そのため画面全体がGeometryReader
の領域になります。GeometryReader
自体にサイズ明示的に指定することでコンテンビューは全体に広がらず中心に配置されるようになります。
GeometryReader { geometry in
Text("Width:\(geometry.size.width)")
Text("Height:\(geometry.size.height)")
}.frame(width: 200,height: 200)
.background(Color.cyan)
では中のビューをGeometryReader
の大きさの半分にするようにしてみます。ですがこれでもビューの重なりは解消されません。
GeometryReader { geometry in
Text("Width:\(geometry.size.width)")
.frame(width: geometry.size.width,height:geometry.size.height / 2)
.background(Color.cyan)
Text("Height:\(geometry.size.height)")
.frame(width: geometry.size.width,height:geometry.size.height / 2)
.background(Color.orange)
}.frame(width: 200,height:200)
綺麗に重ならずに表示させるためにはoffset
などを使用して明示的に座標をずらすかVStack
などで囲う必要があります。
GeometryReader { geometry in
Text("Width:\(geometry.size.width)")
.frame(width: geometry.size.width,height:geometry.size.height / 2)
.background(Color.cyan)
Text("Height:\(geometry.size.height)")
.frame(width: geometry.size.width,height:geometry.size.height / 2)
.background(Color.orange)
.offset(y:geometry.size.height / 2)
}.frame(width: 200,height:200)
GeometryReader { geometry in
VStack(spacing:0){
Text("Width:\(geometry.size.width)")
.frame(width: geometry.size.width,height:geometry.size.height / 2)
.background(Color.cyan)
Text("Height:\(geometry.size.height)")
.frame(width: geometry.size.width,height:geometry.size.height / 2)
.background(Color.orange)
}
}.frame(width: 200,height:200)
ScrollViewと実装する際の注意点
ScrollView
とGeometryReader
を合わせて実装する場合はどちらを親にするかで注意が必要です。ScrollView
を上にGeometryReader
を下に実装した場合はスクロール機能が動作しなくなってしまいます。
ScrollView {
GeometryReader { geometry in
VStack{
ForEach(1...100, id: \.self) {
Text("Item \($0)")
.padding()
}
}
}
}
なのでGeometryReader
を上にScrollView
を下に実装することで解決することができます。
GeometryReader { geometry in
ScrollView {
VStack{
ForEach(1...100, id: \.self) {
Text("Item \($0)")
.padding()
}
}
}
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。