现有答案涵盖了您只想更改一次状态栏颜色的情况(例如,在整个应用程序中使用浅色内容),但如果您想以编程方式执行此操作,则首选项键是实现此目的的一种方法。
可以在下面找到完整的示例,但这里是对我们将要做的事情的描述:
- 定义一个符合
PreferenceKey 的结构,Views 将使用它来设置他们喜欢的状态栏样式
- 创建
UIHostingController 的子类,它可以检测偏好更改并将它们桥接到相关的 UIKit 代码
- 添加扩展
View 以获得几乎看起来是官方的 API
偏好键一致性
struct StatusBarStyleKey: PreferenceKey {
static var defaultValue: UIStatusBarStyle = .default
static func reduce(value: inout UIStatusBarStyle, nextValue: () -> UIStatusBarStyle) {
value = nextValue()
}
}
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")
}
}
查看扩展
extension View {
func statusBar(style: UIStatusBarStyle) -> some View {
preference(key: StatusBarStyleKey.self, value: style)
}
}
用法
首先,在您的 SceneDelegate 中,您需要将 UIHostingController 替换为您的子类:
//Previously: window.rootViewController = UIHostingController(rootView: rootView)
window.rootViewController = HostingController(wrappedView: rootView)
现在任何视图都可以使用您的扩展来指定他们的偏好:
VStack {
Text("Something")
}.statusBar(style: .lightContent)
注意事项
this answer 对另一个问题提出了使用 HostingController 子类来观察偏好键变化的解决方案 - 我之前使用过有很多缺点的 @EnvironmentObject,偏好键似乎更适合这个问题。
这是解决此问题的正确方法吗?我不知道。可能存在无法处理的边缘情况,例如,如果层次结构中的多个视图指定了首选项键,我还没有彻底测试以查看哪个视图获得优先级。在我自己的使用中,我有两个相互排斥的视图,它们指定了它们首选的状态栏样式,所以我不必处理这个问题。因此,您可能需要对其进行修改以满足您的需要(例如,可能使用一个元组来指定样式和优先级,然后让您的 HostingController 在覆盖之前检查它的先前优先级)。