如何在 SwiftUI 应用生命周期中更改状态栏样式?
Posted
技术标签:
【中文标题】如何在 SwiftUI 应用生命周期中更改状态栏样式?【英文标题】:How to change StatusBarStyle on SwiftUI App Lifecycle? 【发布时间】: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
属性。这将在您的第一个视图上定义,所有子视图都将继承此属性。这个答案可能会有所帮助:***.com/questions/59569503/…
感谢您的评论。要执行您链接的内容,我需要访问场景委托,这正是我所说的我无权访问,因为没有创建。你知道如何在没有 SceneDelegate.swift 的情况下访问这个委托吗?
如果你需要一些 UIKit 级别的自定义,你应该使用 UIKit 生命周期。实际上,目前 SwiftUI 生命周期根本无法配置。从行为的角度来看,这些方法之间没有区别,SwiftUI Life Cycle 只是场景管理的内置包装器。
Asperi,我会照你说的做。我将使用 UIKit 生命周期为 iOS 创建一个新项目,并为 MacOS 创建一个新项目。 SwiftUI Life Cycle 没有准备好,或者文档还不够完善。
@MoacirBraga 你有没有找到使用 SwiftUI App Lifecycle 以编程方式更改状态栏颜色的解决方案?
【参考方案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以上是关于如何在 SwiftUI 应用生命周期中更改状态栏样式?的主要内容,如果未能解决你的问题,请参考以下文章
如何更改应用程序色调颜色(新的 SwiftUI 生命周期应用程序)?
如何在 Xcode 12 的新 SwiftUI App 生命周期中更改 window.rootViewController?
如何在新的 SwiftUI 应用生命周期中接受 CloudKit 共享?
如何将 CoreSpotlight 与 Swiftui 应用生命周期进行深度链接?