概要
タイトルの通り、SwiftUI で、画面の幅と高さを取得する方法についての記事です。
なお、「いや、UIScreen.main.bounds で取得できるやろ、何言っとるんやお前」と思われた方もいらっしゃると思いますが、UIScreen.main.bounds
だと、iPadOS で SplitView した際などに画面幅の変更が反映されないため、本記事を書きました。
ざっくりとした実装方針
- 画面サイズをグローバルで保存するため、ObservableObject に準拠したクラスを作成
- ContentView で、子 View を GeometryReader でラップ
-
.onChange
モディファイアを使って、GeometryReader を使って取得した画面サイズを1. で作成したストアに保存
1. 画面サイズを保存するクラスを作成
ObservableObject に準拠した、画面サイズを保存するクラスを作成します。 update
関数を作ったのは、使用側で ScreenSizeStore を書き換えるような書き方をしたくなかったのが理由です。
import Foundation
import Combine
import SwiftUI
final class ScreenSizeStore: ObservableObject {
@Published private(set) var screenWidth: CGFloat
@Published private(set) var screenHeight: CGFloat
// 一旦デフォルトの画面サイズで初期化
init() {
self.screenWidth = UIScreen.main.bounds.width
self.screenHeight = UIScreen.main.bounds.height
}
func update(width: CGFloat, height: CGFloat) {
self.screenWidth = width
self.screenHeight = height
}
}
2. ContentView に GeometryReader 設置
ContentView に、GeometryReader を設置します。ContentView の親となる App に置くと色々面倒そうだと思ったので、ContentView にしています。
import SwiftUI
struct ContentView: View {
var body: some View {
GeometryReader { rootViewGeometry in
// 子 View
HogeView()
}
}
}
3. .onChange
を使い、ScreenSizeStore
を更新
ContentView に、@EnvironmentObject
の宣言の設置と、子 View への .onChange
モディファイアの追加を行います。
import SwiftUI
struct ContentView: View {
@EnvironmentObject var screenSizeStore: ScreenSizeStore
var body: some View {
GeometryReader { rootViewGeometry in
RootView()
.onAppear {
screenSizeStore.update(width: rootViewGeometry.size.width, height: rootViewGeometry.size.height)
}
.onChange(of: rootViewGeometry.size) { updatedValue in
screenSizeStore.update(width: updatedValue.width, height: updatedValue.height)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(ScreenSizeStore())
}
}
最後に、App 側で、ContentView
に .environmentObject
モディファイアを追加する必要があります。(追加しないとエラーが出る)
import SwiftUI
@main
struct Hoge_App: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(ScreenSizeStore())
}
}
}
使用例
使用側は、@EnvironmentObject
の宣言を追加してやるだけです
import SwiftUI
struct CousinView: View {
@EnvironmentObject var screenSizeStore: ScreenSizeStore
var body: some View {
VStack {
HStack {
(中略)
}
.frame(maxWidth: screenSizeStore.width)
}
}
}
最後に
いかがでしょうか?
もし、「もうちょっといいやり方あるぞ」などのご意見あれば、お気軽にコメント欄に投稿していただければ幸いです。