【Swift UI】GeometryReaderの使い方!ビューの幅や高さ、座標を取得する

【Swift UI】GeometryReaderの使い方!ビューの幅や高さ、座標を取得する

この記事からわかること

  • Swift UIGeometryReader使い方
  • ビュー高さ座標取得方法
  • GeometryProxy構造体とは?
  • 列挙型CoordinateSpaceとは?

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

公式リファレンス:GeometryReader

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)
}
【Swift UI】GeometryReaderの使い方!ビューの幅や高さ、座標を取得する

GeometryReaderビュー(GeometryReader自体)の横幅と高さを取得しています。中のTextの大きさではなくGeometryReaderとして確保している領域のサイズや座標を取得できる感じです。

GeometryProxy構造体

公式リファレンス: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) // ビューのローカル座標空間への名前付き参照
}
iOSデバイス画面の座標の見方とX軸Y軸の方向

globallocalの違いはその名の通り基準とする座標の違いになります。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)
【Swift UI】GeometryReaderの使い方!ビューの幅や高さ、座標を取得する

大きさを指定しないと重なってしまう

GeometryReaderでは内部に設置したビューに対して明示的にサイズや位置を指定しないとビューが重なってしまいます

【Swift UI】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と実装する際の注意点

ScrollViewGeometryReaderを合わせて実装する場合はどちらを親にするかで注意が必要です。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()
            }
            
        }
    }
}

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index