I already spent a lot of hours trying to figure out a way to change statusBarStyle to light/dark using the new lifecycle SwiftUI App.
The newest posts about the status bar teach how to hide it, but I don't want to do it, I just need to change it to dark or light.
To change the color, the most recent way I found is open SceneDelegate.swift and change window.rootViewController to use my own HostingController, but it will only work for projects using UIKit App Delegate Lifecycle. Using SwiftUI App Lifecycle, the SceneDelegate.swift will not be generated, so where can I do it?
I can do it via General Settings on the Xcode interface. My question is about how to do it via code dynamically.
Below is what I got so far.
Everything.swift
import Foundation
import SwiftUI
class LocalStatusBarStyle { // style proxy to be stored in Environment
fileprivate var getter: () -> UIStatusBarStyle = { .default }
fileprivate var setter: (UIStatusBarStyle) -> Void = {_ in}
var currentStyle: UIStatusBarStyle {
get { self.getter() }
set { self.setter(newValue) }
}
}
struct LocalStatusBarStyleKey: EnvironmentKey {
static let defaultValue: LocalStatusBarStyle = LocalStatusBarStyle()
}
extension EnvironmentValues { // Environment key path variable
var localStatusBarStyle: LocalStatusBarStyle {
get {
return self[LocalStatusBarStyleKey.self]
}
}
}
class MyHostingController<Content>: UIHostingController<Content> where Content:View {
private var internalStyle = UIStatusBarStyle.default
@objc override dynamic open var preferredStatusBarStyle: UIStatusBarStyle {
get {
internalStyle
}
set {
internalStyle = newValue
self.setNeedsStatusBarAppearanceUpdate()
}
}
override init(rootView: Content) {
super.init(rootView:rootView)
LocalStatusBarStyleKey.defaultValue.getter = { self.preferredStatusBarStyle }
LocalStatusBarStyleKey.defaultValue.setter = { self.preferredStatusBarStyle = $0 }
}
@objc required dynamic init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
struct TitlePage: View {
@Environment(\.localStatusBarStyle) var statusBarStyle
@State var title: String
var body: some View {
Text(title).onTapGesture {
if self.statusBarStyle.currentStyle == .darkContent {
self.statusBarStyle.currentStyle = .default
self.title = "isDefault"
} else {
self.statusBarStyle.currentStyle = .darkContent
self.title = "isDark"
}
}
}
}
struct ContainerView: View {
var controllers: [MyHostingController<TitlePage>]
init(_ titles: [String]) {
self.controllers = titles.map { MyHostingController(rootView: TitlePage(title: $0)) }
}
var body: some View {
PageViewController(controllers: self.controllers)
}
}
struct PageViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
return pageViewController
}
func updateUIViewController(_ uiViewController: UIPageViewController, context: Context) {
uiViewController.setViewControllers([controllers[0]], direction: .forward, animated: true)
}
typealias UIViewControllerType = UIPageViewController
}
MyApp.swift
import SwiftUI
@main
struct TestAppApp: App {
var body: some Scene {
WindowGroup {
ContainerView(["Subscribe", "Comment"])
}
}
}
struct TestAppApp_Previews: PreviewProvider {
static var previews: some View {
Text("Hello, World!")
}
}
Add two values to the Info.plist:
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
This works with the new SwiftUI App Lifecycle (@main). Verified on iOS14.4.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With