画面のビュー要素のサイズを取得する

画面のビュー要素のサイズを取得する方法を紹介します。

Fig. 1 セーフエリアが上と下にある機種
Fig. 2 セーフエリアが上にしかない機種

スクリーンのサイズは、7行目、8行目、に示すように UIScreen.main.bounds.size で取得できます。

ビューを主に配置する領域のサイズは GeometryReader をつかって取得します。この例では ZStack が占める領域のサイズを取得したいので、ソースコードにおいて ZStack を GeometryReader で囲んでいます。

領域を示すプロパティは自動的に geom インスタンスに格納されるので、それを参照します。16行目、17行目が geom インスタンスを参照して ZStack のサイズを取得しているところです。

ちなみに geom はユーザの好みでつけるインスタンス名で、the_geom や my_geom など、任意の名前をつけてください。View の中で複数の GeometryReader を使う時は geom0, geom1, geom2 など、わかりやすいインスタンス名をつけるのが良いでしょう。

ZStack が占める領域以外は、セーフエリアと定義されています。iPhone14 などの機種はセーフエリアが上と下に準備されています。iPhoneSE などの機種はセーフエリアが上にだけ準備されています。

上側のセーフエリアの高さは、geom の safeAreaInsets.top を参照します。
下側のセーフエリアの高さは、geom の safeAreaInsets.bottom を参照します。

それぞれ実行した結果を計算してみましょう。上側のセーフエリアの高さと、ZStackの高さと、下側のセーフエリアの高さが、スクリーンの高さになるはずです。

計算は (( geom.safeAreaInset.top ) + ( geom.size.height ) + ( geom.safeAreaInset.bottom )) で実施します。

iPhone14 の場合は 47 + 763 + 34 で 844 になります。これはスクリーンの高さと同じです。
iPhoneSE の場合は 20 + 647 + 0 で 667 になります。これはスクリーンの高さと同じです。

import SwiftUI

struct ContentView: View {

    var body: some View {

        let scr_w = UIScreen.main.bounds.size.width
        let scr_h = UIScreen.main.bounds.size.height

        GeometryReader {

            geom in

            ZStack{

                let geom_w = geom.size.width
                let geom_h = geom.size.height

                let rct_w0 = geom_w * 1.00
                let rct_h0 = geom_h * 1.00

                let rct_w1 = geom_w * 0.75
                let rct_h1 = geom_h * 0.75

                let rct_w2 = geom_w * 0.50
                let rct_h2 = geom_h * 0.50

                Rectangle()
                    .frame( width: rct_w0, height: rct_h0 )
                    .foregroundColor( .green )

                Rectangle()
                    .frame( width: rct_w1, height: rct_h1 )
                    .foregroundColor( .blue )

                Rectangle()
                    .frame( width: rct_w2, height: rct_h2 )
                    .foregroundColor( .cyan )

                VStack{

                    let gsai_top = geom.safeAreaInsets.top
                    let gsai_btm = geom.safeAreaInsets.bottom

                    Text( String( format: "screen: %.2f * %.2f", scr_w, scr_h ) )
                    Text( String( format: "rct0: %.2f * %.2f", rct_w0, rct_h0 ) )
                    Text( String( format: "rct1: %.2f * %.2f", rct_w1, rct_h1 ) )
                    Text( String( format: "rct2: %.2f * %.2f", rct_w2, rct_h2 ) )
                    Text( String( format: "safeAreaInsets.top: %.2f", gsai_top ) )
                    Text( String( format: "safeAreaInsets.bottom: %.2f", gsai_btm ) )
                    Text( String( format: "debug: %.2f", gsai_top + rct_h0 + gsai_btm ) )

                }

            }

        }

    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

ちなみに iPhone を回転させたときは下に示すような画面になります。

GeometryReader は、回転も考慮してビューの要素の寸法を示す geom インスタンスを提供してくれますが、スクリーンのサイズを示す UIScreen.main.bounds.size は、縦方向であっても横方向であっても width と height の値は同じになります。

Swift のプログラミングをするまで意識したことがなかったのですが、iPhoneSE は横位置にした場合セーフエリアがなくなるのですね。