背景
SwiftUI実装している時に、画面サイズに応じて文字や画像サイズを変更したいと思い色々調べていたところGeometryReader
とUIScreen.main.bounds
という2つのサイズ取得方法が出てきたので備忘がてら残しておこうと思います。
GeometryReader
Viewの一種であり、VStackやHStack等と同様にView階層内に宣言をする必要がある。
GeometryProxyから以下の情報を取得することができる。
・自Viewのサイズ
・親Viewからの位置情報(x軸,y軸)
サンプル
これだけだとイメージがつかないと思うので、実際に画面イメージを見ていきたいと思います。
View構成
ContentViewに子Viewが8個ある。
ContentView(親View)
└ DetailCellView(子View)
- 🔴 左上の赤丸部分
子ViewのX,Y座標を表す基準地点。
ここをx=0, y=0として子Viewの座標位置をGeometryProxyで取得する。 - 🔵 青丸の部分
DetailCellViewの一番右上のViewのX軸、Y軸の地点。
この場合は基準地点からX軸方向に201、Y軸方向に62移動した地点が親Viewからの位置としてGeometryProxyで取得できる。 - Width / Height
DetailCellViewのサイズ。
位置情報(X,Y軸)と異なり子Viewのサイズが同じであれば全て同じサイズとなる。
ちなみに座標位置や子Viewのサイズは以下で取得可能です。
Text("X: \(Int(geometry.frame(in: .global).minX))")
Text("Y: \(Int(geometry.frame(in: .global).minY))")
Text("Width: \(Int(geometry.size.width))")
Text("Height: \(Int(geometry.size.height))")
UIScreen.main(非推奨)
端末全体の画面サイズ(ナビバー、タブバーを含めた物理的な画面サイズ)
つまり端末(iPhone 16, iPad 13 etc)によりサイズは固定となっている。
★UIScreen.mainは非推奨になった★
マルチウィンドウ対応を想定して非推奨になった。(と思われる。)
iOS13以降は複数ウィンドウを持つアプリもサポートするようになった。
それにより端末ごとの固定サイズを設定するとレイアウトが崩れてしまう可能性が出てくる。
例えば、
1. iPadでアプリを2画面表示(SplitView)
2. iPhoneでApp ClipやQuick Noteのような一時的ウィンドウ
3. macOS Catalystでウィンドウを複数開くアプリ
UIScreen.mainではデバイスのメイン画面を指しているため、
- マルチウィンドウで表示している時
- 外部ディスプレイに表示している時
でも常にアプリインストールしている端末のサイズ固定になってしまい、現在のウィンドウが表示されている値と無関係の値となるためレイアウトが崩れてしまう。
どうするか
.connectedScenesを使う
.connectedScenesとは、アプリが今接続しているsceneを返すプロパティです。
具体的には、動作している可能性があるsceneを位置に限らず返します。位置に限らずとは前画面にあるか背景にあるか画面外にあるかに限らずという意味です。
ただし、複数のUIWindowSceneが入っている可能性があるため、必ずしもfirst
で欲しい値が取得できるとは限らないので注意が必要です。
let window = UIApplication.shared.connectedScenes.first as? UIWindowScene
window.screen.bounds.width
まとめ
-
GeometryReader
Viewに親子関係があり、親Viewからの位置や子View自身のサイズを取得したい場合に使用する。 -
UIScreen.main.bounds
Apple非推奨となったため原則使用しない方が無難。 -
.connectedScenes
画面サイズを取得したい場合は従来のUIScreen.main.boundsの代わりにこちらを利用する。
参考