SwiftUIでビューの大きさを動的に取得する

SwiftUIでアプリ実行中に動的にビューの大きさを取得するには、GeometryReaderと組み合わせる方法があります。

この記事では具体的な方法とコード例を解説します。

目次

ビューのサイズをコンソールに出力する

GeometryReaderと組み合わせて、ビューの大きさを取得するには、次のようなコードを書きます。

struct ContentView: View {

    var body: some View {
        Text("Hello, world!")
            .padding()
            .background() {
                GeometryReader { geometry in
                    Path { path in
                        print("Text frame size = \(geometry.size)")
                    }
                }
            }
    }
}

このコードを実行すると、次のようにコンソールにTextの大きさが出力されます。

Text frame size = (126.66666666666666, 52.33333333333333)

背景にGeometryReaderを置く

SwiftUIで自分自身の大きさを知ることが出来るビューはGeometryReaderです。そこで、backgroundモディファイアを使って、大きさを取得したいビューのにGeometryReaderを配置します。backgroundモディファイアを使って配置すると、GeometryReaderはそのビューの大きさと同じ大きさになります。

geometryGeometryProxyという構造体です。GeomertyProxy.sizeプロパティにビューの大きさが入っているので、その値を取得します。

オーバーレイでも可能

backgroundモディファイアの代わりにoverlayモディファイアを使っても同様に大きさを取得できます。状況によって使い分けるのも良いと思います。

実装例 ラベルに表示する

実際に使ったコードを実装してみます。コンソールに出力するのではなく、別のTextに値を表示するというコードを実装してみましょう。次のようなコードになります。

struct ContentView: View {
    @State private var labelSize: CGSize = CGSize()

    var body: some View {
        VStack {
            Text("Hello, world!")
                .padding()
                .background() {
                    GeometryReader { geometry in
                        Path { path in
                            let size = geometry.size
                            DispatchQueue.main.async {
                                if self.labelSize != size {
                                    self.labelSize = size
                                }
                            }
                        }
                    }
            }
            
            Text("Label Size : \(String(format: "%.0f x %.0f", labelSize.width, labelSize.height))")
        }
    }
}

このコードを実行すると、次のように2つラベルが表示され、上側のラベルの大きさが、下側のラベルに表示されます。

ラベルに大きさを表示した例
ラベルに大きさを表示した例

ビュー構築中に取得したサイズを使ってビューを変更できない

Textに取得した大きさを表示するため、取得した大きさはlabelSizeプロパティに代入しています。この値を使ってTextの表示内容を変更したいので、@Stateアトリビュートを付けて、SwiftUIにlabelSizeを管理させています。

注意しなければいけないのは、GeometryReaderによってビューの大きさを取得した瞬間は、まだ、ビューの構築処理中です。labelSizeプロパティが変更されると、ビューの再構築が必要になりますが、再構築中に変更すると再構築ができず、エラーとなり、取得した値を使ってTextを表示することができません。

実際に行ってみると、Xcodeのライブプレビューでは動作するのですが、iOSシミュレータや実機で実行するとサイズが表示されません。

iOSシミュレータや実機では0 x 0になる
iOSシミュレータや実機では0 x 0になる

iOSシミュレータや実機で実行すると、Xcodeにも次のようなエラーメッセージが表示されます。

Xcodeからのエラー指摘
Xcodeからのエラー指摘

Modifying state during view update, this will cause undefined behavior.

そこで、DispatchQueue.main.asyncメソッドを使って、再構築処理が終わった後にプロパティに値を代入させるようにしています。プロパティが更新されると、ビューの再構築が行われます。

iOSシミュレータや実機でも動作した
iOSシミュレータや実機でも動作した

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

アプリ開発が好きなアプリ開発者。このブログは学習メモを記事にしたテックブログです。仕事ではアプリ開発をメインに、技術書の執筆やセミナーの講師などもしています。業務や著書のサイトはこちらです→ アールケー開発

目次