【问题标题】:How can I change the status bar text color per view in SwiftUI?如何在 SwiftUI 中更改每个视图的状态栏文本颜色?
【发布时间】:2020-05-26 19:30:31
【问题描述】:

我正在寻找一种方法来更改状态栏的 文本 颜色,以便为每个视图使用不同的文本颜色。

我见过this Q&A,但这不是我要找的。我不是在寻找只允许所有视图使用一种状态栏文本颜色的解决方案。我想为每个视图更改状态栏文本颜色。例如,一个视图可能有深色背景,因此我需要浅色文本。我可能会导航到具有浅色背景的另一个视图,所以现在我需要深色文本。建议的重复答案只返回.lightContent,这意味着当我移动到不同的视图时,状态栏文本颜色无法动态变化。

This answer here 在我的机器上工作,但性能不佳。它下面的评论证实了这一点。滞后是不可接受的,所以这个解决方案不好。

到目前为止我看到的其他解决方案会导致此特定错误:

Compiling failed: extensions of generic classes cannot contain '@objc' members

我还尝试在自定义控制器中使用环境对象:

import SwiftUI

/// Allows for the status bar colors to be changed from black to white on the dark gray title bar
class Controller<ContentView> : UIHostingController<ContentView> where ContentView : View {
    @EnvironmentObject var statusBarTextColor: StatusBarTextColor

    lazy var isDark: Bool = self.statusBarTextColor.isDark

    override var preferredStatusBarStyle: UIStatusBarStyle {
        return isDark ? .lightContent : .darkContent
    }
}

这会导致错误:

Thread 1: Fatal error: No ObservableObject of type StatusBarTextColor found. A View.environmentObject(_:) for StatusBarTextColor may be missing as an ancestor of this view.

在我的 SceneDelegate 文件中,我确实指定了 StatusBarTextColor environmentObject:

            window.rootViewController = Controller(
                rootView: Home()
                    .environmentObject(PostData())
                    .environmentObject(CardPosition())
                    .environmentObject(StatusBarTextColor())
            )

这就是 ObservableObject 本身:

import Combine
import SwiftUI

final class StatusBarTextColor: ObservableObject {
    @Published var isDark: Bool = true
}

如果我猜测为什么这不起作用,我会说这是因为控制器在 StatusBarTextColor 可用之前被初始化。

我越是研究这个问题,就越觉得没有解决办法。我几乎浏览了有关该主题的所有文章、答案和视频。他们要么使用一个控制器只返回 .lightContent,要么使用故事板和多个控制器,这不是我正在使用的。

【问题讨论】:

    标签: ios swift swiftui statusbar


    【解决方案1】:

    您可以使用您找到的解决方案here,但您可以创建一个名为 onWillDisappear 的视图修改器来公开 viewWillDisappear,而不是使用 onDisappear,它会延迟颜色更改直到视图完全消失。颜色变化会尽快发生。

    用法:

    struct MyClass: View {
          @Environment(\.localStatusBarStyle) var statusBarStyle
        // ...
            SomeView()
            }.onAppear {
                self.statusBarStyle.currentStyle = .darkContent
            }
            .onWillDisappear {
                self.statusBarStyle.currentStyle = .lightContent
            }
    }
    

    代码:

       import SwiftUI
        
        
        class HostingController<Content>: UIHostingController<Content> where Content: View {
            private var internalStyle = UIStatusBarStyle.lightContent
        
            @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)
            }
        }
        
        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) }
            }
        }
        
        // Custom Environment key, as it is set once, it can be accessed from anywhere
        // of SwiftUI view hierarchy
    
    struct LocalStatusBarStyleKey: EnvironmentKey {
        static let defaultValue: LocalStatusBarStyle = LocalStatusBarStyle()
    }
    
    extension EnvironmentValues { // Environment key path variable
        var localStatusBarStyle: LocalStatusBarStyle {
            get {
                return self[LocalStatusBarStyleKey.self]
            }
        }
    }
    
    struct WillDisappearHandler: UIViewControllerRepresentable {
        func makeCoordinator() -> WillDisappearHandler.Coordinator {
            Coordinator(onWillDisappear: onWillDisappear)
        }
    
        let onWillDisappear: () -> Void
    
        func makeUIViewController(context: UIViewControllerRepresentableContext<WillDisappearHandler>) -> UIViewController {
            context.coordinator
        }
    
        func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<WillDisappearHandler>) {
        }
    
        typealias UIViewControllerType = UIViewController
    
    class Coordinator: UIViewController {
            let onWillDisappear: () -> Void
    
            init(onWillDisappear: @escaping () -> Void) {
                self.onWillDisappear = onWillDisappear
                super.init(nibName: nil, bundle: nil)
            }
    
            required init?(coder: NSCoder) {
                fatalError("init(coder:) has not been implemented")
            }
    
            override func viewWillDisappear(_ animated: Bool) {
                super.viewWillDisappear(animated)
                onWillDisappear()
            }
        }
    }
    struct WillDisappearModifier: ViewModifier {
        let callback: () -> Void
    
        func body(content: Content) -> some View {
            content
                .background(WillDisappearHandler(onWillDisappear: callback))
        }
    }
    
    extension View {
        func onWillDisappear(_ perform: @escaping () -> Void) -> some View {
            self.modifier(WillDisappearModifier(callback: perform))
        }
    }
    

    使用 onWillDisappear 代码查看原始帖子here

    【讨论】:

      【解决方案2】:

      在您的SceneDelegate 中,您将StatusBarTextColor() 注入Home 视图。但是,您在 Controller 中声明了 EvironmentObject。

      【讨论】:

      • 确实如此,但根据我的尝试,尝试像我为 Home 所做的那样尝试将 StatusBarTextColor 注入 Controller 是行不通的。你知道这样做的方法吗?
      • 是的,.environmentObject 是一个 view 修饰符。 Controller 不是 View
      • 这是否意味着根本无法在Controller 中使用@EnvironmentObject
      • 您必须找到另一种注入依赖项的方法。尝试使用 EnvironmentObject 作为参数覆盖初始化程序。但是,看到 @EnvironmentObject 在视图之外使用时不会做任何事情,您可以完全保留它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-27
      • 1970-01-01
      • 2022-11-11
      • 1970-01-01
      • 2013-07-14
      • 2020-03-25
      相关资源
      最近更新 更多