SwiftUIでステータスバーの色を変える方法4つ
SwiftUIではUIViewControllerのpreferredStatusBarStyle
に代わるものが存在しないため、ステータスバーの色を自在に切り替えることができません。
そんな中、いくつかステータスバーの色を変える方法を検討してみたので、もしステータスバーの色を変えたかったら目的に合った方法でやってもらえればいいかも。
Info.plistで指定する方法
もしアプリ内でステータスバーの色を切り替える必要がない場合は従来通りInfo.plistで指定できます。
preferredColorScheme
を使う方法
これはSwiftUIにおいて、現在の設定に関わらず、常にダークモード or ライトモードどちらかを使用するように指定するModifierです。
struct ContentView: View {
var body: some View {
ZStack {
Text("Hello, world!")
.padding()
}
.preferredColorScheme(.dark)
}
}
こういう風にViewをダークモードで表示させれば、ステータスバーも白くなるでしょという。
ただ、これをしてしまうとステータスバーの色だけでなく、Viewの色までダークモード時の物になってしまいますし、なかなか思うようにステータスバーの色が変わってくれないことがあって不安定なので、全然お勧めしません。preferredColorScheme
は親Viewにまで影響するので制御も難しいですし。
一応、子ViewにcolorScheme
Modifierを使って、preferredColorScheme
は.dark
だけれど子Viewは.light
で表示するといったこともできますが、Environmentで取れるcolorScheme
の値がpreferredColorScheme
に引っ張られて、ユーザーがダークモードを設定しているかどうか取得ができないので、ユーザーの設定によって切り替えることができません。
独自UIWindowを追加する
iOSアプリはウインドウが複数ある時、最前面にある全画面のウインドウからステータスバーの色を決めます。
なので、最前面に透明なUIWindowを用意して、そのRootViewControllerのpreferredStatusBarStyle
を触るとステータスバーの色を変えれます。
SwiftUI Change Status Bar Color with UIWindow. GitHub Gist: instantly share code, notes, and snippets.
詳細は上記のソースコードを見てください。
StatusBarConfigurator
がUIWindowを保持しておくクラスで、これは各Sceneごとに1つずつじゃないと変なことになるので、上の階層のViewで定義したらEnvironmentObjectで下の階層のViewに伝搬させるのがいいでしょう。
また、UIWindowを生成するのに現在のUISceneが必要で、それを取得するためにUIViewRepresentableを使ってます(SceneFinder)。これをSwiftUIのbackground
に指定したら、UIViewの中でUIWindowを辿ってUIWindowSceneを取得できるという算段です。
もし、アプリをiPadのマルチウインドウに対応させる必要がないなら、UISceneをUIApplication.shared.connectedScenes
から引っ張ってきて、StatusBarConfigurator
をシングルトンにしてしまってもいいかもしれません。
この方法だとSwiftUI Lifecycleのまま簡単に使用できるのですが、sheet
やfullScreenCover
でモーダル表示されるViewごとに設定を変えるってことが困難なので、用途によっては次の方法がいいかもしれません。
UIHostingControllerのサブクラスを作る
諦めの境地です。SwiftUI Life Cycleでは無理なので、UIKit Life Cycleを使い、preferredStatusBarStyle
をoverrideしたUIHostingControllerを作り、UIWindowのRoot View Controllerに指定します。
SwiftUI Change Status Bar Color with Custom Hosting Controller - AppDelegate.swift
自分が書くとしたらこんな感じかなー
Swift UI Life Cycleで作ったプロジェクトも、@main
のついたstructを消して、@main
のついたAppDelegate
と差し替えれば簡単にUIKit Life Cycleに変更することができます。
あとはpreferredStatusBarStyle
をoverrideしたUIHostingControllerを作り、StatusBarConfigurator
というObservableObjectで囲ってEnvironmentObjectで子Viewに伝搬させる形です。
この方法だと、sheet
を表示した時とかステータスバーの色が自動で切り替わってくれるのですが、@SceneStorage
やHandOffで使うuserActivity
など、SwiftUI Life Cycleでしか使えないものもあるので、正直微妙なところです。
まとめ
個人的にはSwiftUIはまだまだ表現力が足りませんね。UIKitと併用するのが良さそうです。
preferredColorScheme
を使うのはstack overflowで結構すぐに出てくる方法なのですが、安定しないのとステータスバー以外にも影響が出てしまうので正直使い物になりません。僕としてはUIHostingControllerのサブクラスを作る方法が一番おすすめです。SwiftUI Life Cycleが使えなくなりますが、AppDelegateやSceneDelegateを足してもそんなに行数増えないですし、SwiftUI Life Cycleのままなんとかする方法を悩むよりはちゃっちゃとUIKit Life Cycle使ってしまったほうが絶対に楽ですから。
ただ、SwiftUI Life Cycleじゃないと使えないものもあるし、そういうの天秤にかけて一番いい方法を選んでいただければいいなと思います。
コメント