SwiftUI:状态栏颜色

kee*_*n3d 10 ios swift swiftui

有没有一种方法可以将SwiftUI视图的状态栏更改为白色?

我可能缺少一些简单的东西,但是似乎找不到在SwiftUI中将状态栏更改为白色的方法。到目前为止,我只是看到.statusBar(hidden: Bool)

Dan*_*and 33

状态栏文本/色调/前景颜色可以通过设置被设置为白色View.dark.light使用模式的配色方案.preferredColorScheme(_ colorScheme: ColorScheme?)

层次结构中使用此方法的第一个视图将优先。

例如:

var body: some View {
  ZStack { ... }
  .preferredColorScheme(.dark) // white tint on status bar
}
Run Code Online (Sandbox Code Playgroud)
var body: some View {
  ZStack { ... }
  .preferredColorScheme(.light) // black tint on status bar
}
Run Code Online (Sandbox Code Playgroud)

  • 我相信这将完全改变配色方案,而不仅仅是状态栏文本颜色。如果您的应用程序使用深色主题,那么它不是一个可行的解决方案 (19认同)
  • 谢谢。这是真正的 SwiftUI 方式。 (3认同)
  • 很好的答案,但它可能会导致更多麻烦,因为这也会改变列表和堆栈内其他对象的颜色方案。仅更改 UIStatusBar 将避免这种情况,并且从技术上讲可以减少代码量。 (2认同)

Muv*_*otv 21

在 info.plist 中,你可以简单地设置

  • “状态栏样式”到“轻量内容”
  • “查看基于控制器的状态栏外观”为否

无需将任何内容更改为您的代码...

  • 似乎对 SwiftUI 应用程序没有做任何事情。 (6认同)

Ark*_*ann 15

现有答案涵盖了您只想更改状态栏颜色一次的情况(例如,在整个应用程序中使用浅色内容),但如果您想以编程方式执行此操作,那么首选项键是实现此目的的一种方式。

完整的例子可以在下面找到,但这里是我们要做的事情的描述:

  • 定义一个符合 的结构PreferenceKey,这将被Views 用来设置他们首选的状态栏样式
  • 创建一个UIHostingController可以检测偏好更改并将它们桥接到相关 UIKit 代码的子类
  • 添加一个扩展View以获得一个看起来几乎是官方的 API

偏好密钥一致性

struct StatusBarStyleKey: PreferenceKey {
  static var defaultValue: UIStatusBarStyle = .default
  
  static func reduce(value: inout UIStatusBarStyle, nextValue: () -> UIStatusBarStyle) {
    value = nextValue()
  }
}
Run Code Online (Sandbox Code Playgroud)

UIHostingController 子类

class HostingController: UIHostingController<AnyView> {
  var statusBarStyle = UIStatusBarStyle.default

  //UIKit seems to observe changes on this, perhaps with KVO?
  //In any case, I found changing `statusBarStyle` was sufficient
  //and no other method calls were needed to force the status bar to update
  override var preferredStatusBarStyle: UIStatusBarStyle {
    statusBarStyle
  }

  init<T: View>(wrappedView: T) {
// This observer is necessary to break a dependency cycle - without it 
// onPreferenceChange would need to use self but self can't be used until 
// super.init is called, which can't be done until after onPreferenceChange is set up etc.
    let observer = Observer()

    let observedView = AnyView(wrappedView.onPreferenceChange(StatusBarStyleKey.self) { style in
      observer.value?.statusBarStyle = style
    })

    super.init(rootView: observedView)
    observer.value = self
  }

  private class Observer {
    weak var value: HostingController?
    init() {}
  }

  @available(*, unavailable) required init?(coder aDecoder: NSCoder) {
    // We aren't using storyboards, so this is unnecessary
    fatalError("Unavailable")
  }
}
Run Code Online (Sandbox Code Playgroud)

查看扩展

extension View {
  func statusBar(style: UIStatusBarStyle) -> some View {
    preference(key: StatusBarStyleKey.self, value: style)
  }
}
Run Code Online (Sandbox Code Playgroud)

用法

首先,在您SceneDelegate需要替换UIHostingController为您的子类:

//Previously: window.rootViewController = UIHostingController(rootView: rootView)
window.rootViewController = HostingController(wrappedView: rootView)
Run Code Online (Sandbox Code Playgroud)

任何视图现在都可以使用您的扩展来指定其首选项:

VStack {
   Text("Something")
}.statusBar(style: .lightContent)
Run Code Online (Sandbox Code Playgroud)

笔记

this answer to another question中建议了使用 HostingController 子类来观察首选项键更改的解决方案- 我以前使用过 @EnvironmentObject ,它有很多缺点,首选项键似乎更适合这个问题。

这是解决这个问题的正确方法吗?我不知道。可能存在无法处理的边缘情况,例如,如果层次结构中的多个视图指定了一个首选项键,我还没有彻底测试以查看哪个视图获得优先级。在我自己的用法中,我有两个相互排斥的视图,它们指定了它们首选的状态栏样式,所以我不必处理这个问题。因此,您可能需要修改它以满足您的需要(例如,可能使用元组指定样式和优先级,然后HostingController在覆盖之前检查它的先前优先级)。

  • 这适用于仍在使用 UIKit 应用程序生命周期的应用程序。对于使用新的 SwiftUI App main 的应用程序,您有解决方案吗? (2认同)

Ned*_*Ned 14

@Dan Sandland 的回答对我有用,但就我而言,需要将界面保持在.light模式下

ZStack {
    Rectangle()...
    
    VStack(spacing: 0) {
        ...
    }.colorScheme(.light)
}
.preferredColorScheme(.dark)
Run Code Online (Sandbox Code Playgroud)


小智 14

只需将此添加到 info.plist

<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
Run Code Online (Sandbox Code Playgroud)

在 IOS 14、xcode 12 上测试


krj*_*rjw 10

就像在链接的评论中,我在这里编辑了这个问题

但是要回答这个问题并帮助人们直接找到答案:

Swift 5和SwiftUI

对于SwiftUI,创建一个名为HostingController.swift的新swift文件。

import Foundation
import UIKit
import SwiftUI

class HostingController: UIHostingController<ContentView> {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在SceneDelegate.swift中更改以下代码行

window.rootViewController = UIHostingController(rootView: ContentView())
Run Code Online (Sandbox Code Playgroud)

window.rootViewController = HostingController(rootView: ContentView())
Run Code Online (Sandbox Code Playgroud)

  • 如果您有环境对象,则这不起作用。 (7认同)
  • 无论如何要从 SwiftUI 更改状态栏颜色吗?即,如果您显示需要不同状态栏的模式? (6认同)
  • 新的 SwiftUI 生命周期怎么样? (5认同)

小智 9

此解决方案适用于使用新 SwiftUI Lifecycle 的应用程序:

我需要动态更改状态栏文本并且无法访问,window.rootViewController因为SceneDelegateSwiftUI 生命周期不存在。

我终于找到了 Xavier Donnellon 这个简单的解决方案:https : //github.com/xavierdonnellon/swiftui-statusbarstyle

StatusBarController.swift文件复制到您的项目中并将您的主视图包装到一个RootView

@main
struct ProjectApp: App {     
    var body: some Scene {
        WindowGroup {
            //wrap main view in RootView
            RootView {
                //Put the view you want your app to present here
                ContentView()
                    //add necessary environment objects here 
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后您可以通过使用.statusBarStyle(.darkContent).statusBarStyle(.lightContent)视图修饰符或通过UIApplication.setStatusBarStyle(.lightContent)直接调用例如来更改状态栏文本颜色。

不要忘记在 Info.plist 中将“基于控制器的状态栏外观”设置为“是”。

  • 警告:这会破坏 iOS15 中的“onOpenURL”。 (4认同)
  • 这是在 SwiftUI Lifecycle 应用程序上唯一对我有用的东西。感谢分享! (3认同)
  • 使用此解决方案运行我的应用程序时,我在控制台中收到错误“对 &lt;_TtGC7SwiftUI19UIHostingControllerGVS_15ModifiedContentVS_7AnyViewVS_12RootModifier__: 0x14cf0e3a0&gt; 的开始/结束外观转换的不平衡调用。”。有人知道如何解决这个问题吗? (2认同)

Ken*_*ler 6

创建一个名为 的新类HostingController

import SwiftUI

final class HostingController<T: View>: UIHostingController<T> {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        .lightContent
    }
}
Run Code Online (Sandbox Code Playgroud)

在你的SceneDelegate.swift,代替所有出现的UIHostingControllerHostingController


小智 6

更新:看起来上面 Hannes Sverrisson 的答案最接近,但我们的答案略有不同。

上面写的 UIHostingController 子类的答案在 XCode 11.3.1 中不起作用。

对于子类(也处理 ContentView 环境设置),以下确实对我有用:

import SwiftUI

class HostingController<Content>: UIHostingController<Content> where Content : View {
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return .lightContent
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在 SceneDelegate.swift 中,更改window.rootViewController设置确实有效:

window.rootViewController = HostingController(rootView: contentView)
Run Code Online (Sandbox Code Playgroud)


Han*_*son 5

SwiftUI

创建一个主机控制器,DarkHostingController并在其preferredStatusBarStyle上进行设置:

class DarkHostingController<Content> : UIHostingController<Content> where Content : View {
    @objc override dynamic open var preferredStatusBarStyle: UIStatusBarStyle {
        .lightContent
    }
}
Run Code Online (Sandbox Code Playgroud)

并包装SceneDelegate

window.rootViewController = DarkHostingController(rootView: ContentView())
Run Code Online (Sandbox Code Playgroud)

  • @MobileMon,您使用 .edgesIgnoringSafeArea(.top) 来允许您的背景一直到顶部,并且您可以设置该颜色。您可以将其直接放在视图上。 (4认同)
  • 这适用于将文本颜色从黑色更改为白色,但如何更改状态栏的背景颜色? (2认同)

Jus*_*n A 5

这对我有用。将这些行添加到您的 info.plist 文件中。您需要切换顶部设置 ( View controller-based status bar appearance) 来确定您要查找的内容。

在此处输入图片说明