【问题标题】:How to change StatusBarStyle on SwiftUI App Lifecycle?如何在 SwiftUI 应用生命周期中更改状态栏样式?
【发布时间】:2020-11-15 11:15:12
【问题描述】:

我已经花了很多时间试图找出一种方法,使用新的生命周期 SwiftUI 应用程序statusBarStyle 更改为亮/暗。

关于状态栏的最新帖子教如何隐藏它,但我不想这样做,我只需要将其更改为深色或浅色。

要更改颜色,我发现的最新方法是打开 SceneDelegate.swift 并更改 window.rootViewController 以使用我自己的 HostingController ,但它仅适用于使用 UIKit App Delegate Lifecycle 的项目。使用SwiftUI App Lifecycle,不会生成SceneDelegate.swift,请问在哪里做呢?

我可以通过 Xcode 界面上的常规设置来完成。我的问题是关于如何通过代码动态地做到这一点。

  • 目标:iOS 14
  • IDE:Xcode 12 beta 3
  • 操作系统:MacOS 11 Big Sur

以下是我目前得到的。

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!")
    }
}

【问题讨论】:

  • 您可以将自己的statusBarStyle 定义为@EnvironmentKey 属性。这将在您的第一个视图上定义,所有子视图都将继承此属性。这个答案可能会有所帮助:stackoverflow.com/questions/59569503/…
  • 感谢您的评论。要执行您链接的内容,我需要访问场景委托,这正是我所说的我无权访问,因为没有创建。你知道如何在没有 SceneDelegate.swift 的情况下访问这个委托吗?
  • 如果你需要一些 UIKit 级别的自定义,你应该使用 UIKit 生命周期。实际上,目前 SwiftUI 生命周期根本无法配置。从行为的角度来看,这些方法之间没有区别,SwiftUI Life Cycle 只是场景管理的内置包装器。
  • Asperi,我会照你说的做。我将使用 UIKit 生命周期为 iOS 创建一个新项目,并为 MacOS 创建一个新项目。 SwiftUI Life Cycle 没有准备好,或者文档还不够完善。
  • @MoacirBraga 你有没有找到使用 SwiftUI App Lifecycle 以编程方式更改状态栏颜色的解决方案?

标签: ios swift colors swiftui statusbar


【解决方案1】:

向 Info.plist 添加两个值:

<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>

这适用于新的 SwiftUI 应用生命周期 (@main)。在 iOS14.4 上验证。

【讨论】:

  • 这是有效的,应该是公认的答案。我有完全相同的问题。非常感谢
【解决方案2】:

我的建议是您只需创建一个 AppDelegate 适配器并从那里进行任何您需要的自定义。 SwiftUI 将处理 AppDelegate 的创建并管理其生命周期。

创建一个 AppDelegate 类:

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        UIApplication.shared.statusBarStyle = .darkContent
        return true
    }
}

现在在您的应用中:

@main
struct myNewApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            Text("I am a New View")
        }
    }
} 

【讨论】:

    【解决方案3】:

    这并不是一个真正的解决方案,但我能想到的最好的(并最终做的)就是强制应用程序进入暗模式。在 Info.plist 或 NavigationView { ... }.preferredColorScheme(.dark)

    这也会改变状态栏。但是,您将无法更改每个视图的状态栏样式。

    【讨论】:

      【解决方案4】:

      之前,SceneDelegate

      (代码取自SwiftUI: Set Status Bar Color For a Specific View

      func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
              ...
              window.rootViewController = MyHostingController(rootView: contentView)
      }
      

      之后,没有SceneDelegate

      @main
      struct MyApp: App {
          var body: some Scene {
              WindowGroup {
                  MyHostingController(rootView: ContentView())
              }
          }
      }
      

      如果您按照上面链接中的答案并在此处使用@main 应用它,您应该能够实现您的更改。

      【讨论】:

      • 我想我们快到了。这样做,WindowGroup 上的错误消息是“通用结构 'WindowGroup' 要求 'MyHostingController' 符合 'View'”。我需要做什么才能使 MyHostingController 符合 View?
      猜你喜欢
      • 2021-09-18
      • 2017-03-29
      • 2013-09-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多