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じゃないと使えないものもあるし、そういうの天秤にかけて一番いい方法を選んでいただければいいなと思います。
コメント