如何防止 iOS 13 的深色模式更改我应用程序状态栏中的文本颜色?

How do I prevent iOS 13's Dark Mode from changing the text color in my app's status bar?

我的导航栏有 white backgroundColor,我的状态栏使用 dark textColor。当用户将 iOS 主题更改为深色模式时,状态栏会更改为 white 背景上的 white 文本。结果什么都看不到。如何为我的应用禁用此更改?

如果您将应用 info.plist 中的 UIViewControllerBasedStatusBarAppearance 键设置为 YES,您可以覆盖当前呈现的视图控制器中的状态栏样式:

override var preferredStatusBarStyle: UIStatusBarStyle {
    if #available(iOS 13, *) {
        return .darkContent
    } else {
        return .default
    }
}

并调用 setNeedsStatusBarAppearanceUpdate() 方法

你可以写扩展到UIStatusBarStyle:

extension UIStatusBarStyle {
    static var black: UIStatusBarStyle {
        if #available(iOS 13.0, *) {
            return .darkContent
        }
        return .default
    }
}

然后您可以轻松地在您的 ViewControllers 中使用:

override var preferredStatusBarStyle: UIStatusBarStyle {
    .black
}

如果您使用的是 UINavigationController,请像这样覆盖扩展(或您自己的子类)中的 preferredStatusBarStyle(只是覆盖 preferredStatusBarStyle 在你的视图控制器中将不起作用):

extension UINavigationController {
  override open var preferredStatusBarStyle: UIStatusBarStyle {
    guard #available(iOS 13, *) else {
      return .default
    }
    return .darkContent
  }
}

正如 Frank 所说,UIViewControllerBasedStatusBarAppearance 必须在您的 info.plist

中设置为 YES

你可以尝试让你的导航栏一直显示light

if #available(iOS 13.0, *) {
    navigationController?.navigationBar.overrideUserInterfaceStyle = .light
 }

有两种方法可以解决这个问题。在 UINavigationController 后代中定义 childViewControllerForStatusBarStyle 函数 class:

@interface MyNavigationController : UINavigationController
...
@end

@implementation MyNavigationController
...
- (UIViewController *)childViewControllerForStatusBarStyle
{
    return self.topViewController;
}
...
@end

之后需要为每个控制器添加函数preferredStatusBarStyle

第二个选项是为所有控制器定义 preferredStatusBarStyle 函数。但是这个函数不应该位于根控制器中,而是位于 UINavigationController 的后代 class 中。

@interface MyNavigationController : UINavigationController
...
@end

@implementation MyNavigationController
...
- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}
...
@end

但是,即使在这种情况下,也有必要为所有隐藏导航栏的控制器(如果有的话)定义函数preferredStatusBarStyle。

iOS 13 个解决方案

UINavigationControllerUIViewController 的子 class! (谁知道)

因此,当呈现嵌入在导航控制器中的视图控制器时,您实际上并没有呈现嵌入式视图控制器;您正在展示导航控制器! UINavigationController,作为UIViewController的子class,继承了preferredStatusBarStylechildForStatusBarStyle,可以根据需要设置。

以下任何一种方法都应该有效:

  1. 完全退出深色模式

    • 在您的 info.plist 中,添加以下内容 属性:
      • 键 - UIUserInterfaceStyle(又名“用户界面样式”)
      • 值 - 光
  2. 覆盖 UINavigationController

    内的 preferredStatusBarStyle
    • preferredStatusBarStyle (doc) - 视图控制器的首选状态栏样式

    • Subclass 或扩展 UINavigationController

        class MyNavigationController: UINavigationController {
            override var preferredStatusBarStyle: UIStatusBarStyle {
                .lightContent
            }
        }
      

        extension UINavigationController {
            open override var preferredStatusBarStyle: UIStatusBarStyle {
                .lightContent
            }
        }
      
  3. UINavigationController

    内覆盖 childForStatusBarStyle
    • childForStatusBarStyle (doc) - 当系统需要视图控制器用于确定状态栏样式时调用
    • 根据 Apple 的文档,

    "If your container view controller derives its status bar style from one of its child view controllers, [override this property] and return that child view controller. If you return nil or do not override this method, the status bar style for self is used. If the return value from this method changes, call the setNeedsStatusBarAppearanceUpdate() method."

    • 换句话说,如果您不在此处执行解决方案 3,系统将回退到上面的解决方案 2。

    • Subclass 或扩展 UINavigationController

        class MyNavigationController: UINavigationController {
            override var childForStatusBarStyle: UIViewController? {
                topViewController
            }
        }
      

        extension UINavigationController {    
            open override var childForStatusBarStyle: UIViewController? {
                topViewController
            }
        }
      
    • 您可以 return 上面任何您喜欢的视图控制器。我推荐以下之一:

      • topViewController (of UINavigationController) (doc) - 导航堆栈顶部的视图控制器
      • visibleViewController (of UINavigationController) (doc) - 与导航界面中当前可见视图关联的视图控制器(提示:这可以包括“一个视图控制器,它以模态方式呈现在导航控制器本身之上”)

注意:如果您决定子class UINavigationController,请记住通过 IB 中的身份检查器将 class 应用到您的导航控制器。

编辑:删除线编辑已删除作为建议答案的扩展名。其他开发人员指出,他们在 Xcode 11.4 中停止工作,Apple 的 documentation 不鼓励使用这种不明确的行为。

P.S。我的代码使用 Swift 5.1 语法

我做过这样的事情。

我有切换器功能,可以根据显示的视图控制器切换状态栏样式

func toggleLight() {
        self.navigationBar.barTintColor = AppColors.White

        isDarkStyle = false
        setNeedsStatusBarAppearanceUpdate()

    }

这是最重要的部分

override var preferredStatusBarStyle: UIStatusBarStyle {

        if #available(iOS 13.0, *) {
            return isDarkStyle ? .lightContent : .darkContent
        }
        return isDarkStyle ? .lightContent : .default
    }

其中isDarkStyle代表导航栏背景色深色或浅色。如果它是暗的,那么文本(内容)应该是浅色的,如果它是浅色的,那么文本(内容)应该是默认的或者来自 iOS 13 暗。

总结:.lightContent, .darkContent 显示独立于深色模式,正如它应该做的那样。虽然 .default 容易受到暗模式变化的影响!

此扩展程序可帮助您更改状态栏文本颜色并且还支持 iOS13

如果其他人发现上述两个答案都不起作用(就像我所做的那样),那么如果您在 UIWindowScene 中有多个 windows 将使用最顶部 window 而不是关键 window 来确定状态栏外观。

因此,例如,如果您的密钥 window 的 windowLevel 设置为 1 并且辅助 window 的 windowLevel 设置为1.1,UIKit 将(无论出于何种原因)询问 辅助 window 中的视图控制器的状态栏外观,而不是键 [=26] 中的视图控制器=], 即使它的 alpha 是 0.

我们的解决方法是仅在次要 window 处于活动状态时将其置于较高级别,并在其隐藏时将其降低到键 window 以下。