在视图控制器之间更新状态栏样式

7
在我的 info.plist 文件中,我将 View controller-based status bar appearance 设置为 YES
在我的 FirstViewController 中,状态栏是隐藏的。
在我的 SecondViewController 中,我有...
override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
}

override var prefersStatusBarHidden: Bool {
    return false
}

override func viewDidLoad() {
    super.viewDidLoad()

    setNeedsStatusBarAppearanceUpdate()
}

然而,状态栏已经显示出来了,但是是黑色的。

我该如何使其正确更新呢?谢谢。

编辑:

AppDelegate.swift 中也有以下代码:

UIApplication.shared.statusBarStyle = .lightContentdidFinishLaunchingWithOptions 方法中。

4个回答

23

Info.plist 文件中有一个名为 View controller-based status bar appearance 的属性。它应该被设置为 YES。然后在您的 UIViewController 中,您应该覆盖 preferredStatusBarStyle

override var preferredStatusBarStyle : UIStatusBarStyle {
    return .lightContent
}

这里有一件重要的事情需要注意:如果您的视图控制器嵌入了UINavigationController并且您的视图控制器的preferredStatusBarStyle方法没有被调用 - 您将不得不通过编写以下内容来解决这个问题:

extension UINavigationController {
    override open var preferredStatusBarStyle : UIStatusBarStyle {
        return topViewController?.preferredStatusBarStyle ?? .default
    }
}

它所做的就是请求顶级控制器的状态栏样式,并适当地进行更新。


谢谢Valerii。这个扩展名就是我今天早上一直在寻找的答案。 - Zack Shapiro
1
@ZackShapiro 在一个作为导航控制器子视图控制器的情况下,正确管理状态栏样式的方法是设置导航控制器的导航栏的 barStyle - matt
@matt 给我展示一下?提交一个答案? - Zack Shapiro
谢谢,扩展已经在导航控制器中完成了工作。不需要在plist中添加任何内容,也不需要添加导航栏样式。 - M Zubair Shamshad
这段代码应该放在哪里?我需要显式地调用它吗?我的视图控制器嵌入在导航控制器中,在分屏控制器的两个部分中都有,虽然我可以随意更改导航栏样式从.default到.black,但我完全无法说服状态栏将其显示为黑色导航栏上的浅色文本。你有什么建议吗?可能是我漏掉了什么? - randallmeadows
在更改状态栏样式后,您应该调用 setNeedsStatusBarAppearanceUpdate - Valerii Lider

14

在如何管理视图控制器作为导航控制器子类时的状态栏样式方面存在很多误解。

如果导航栏是隐藏的,您的子视图控制器可以实现preferredStatusBarStyle,这样做将正常工作。

如果导航栏正在显示,导航控制器将根据导航栏的barStyle来设置状态栏样式,如果栏样式为.default,则设置为.default,如果栏样式为.black,则设置为.lightContent。因此,在导航栏正在显示时,您的视图控制器正确设置状态栏样式的方式是设置导航控制器的导航栏样式。

显然,在viewWillAppear中进行此操作是很合适的,因为每当此视图控制器成为导航控制器堆栈的顶部时,都会调用该方法:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.navigationBar.barStyle = .black // or .default
}

由于这是一个常常引起误解的问题,提供一些代码示例对我和未来遇到此类问题的人会很有帮助。根据我的研究,目前还没有人给出一个好的答案,因此我感到困惑。 - Zack Shapiro
但是,当您在控制器的viewDidLoad或viewDidAppear方法中覆盖状态栏样式时,通过添加UIApplication.shared.statusBarStyle = .default //或.light,状态栏会很好地更改。 - MhmdRizk
@MhmdRizk 看起来可能有效,但我正在展示苹果希望你做什么。这非常不好/未记录,但我从苹果开发者论坛上的一位苹果员工那里学到了这种技巧。导航控制器通过根据导航栏的样式设置状态栏样式自动响应。这使得每个视图控制器负责状态栏样式,这是现代架构。因此,最好这样做,而不是直接与应用程序交谈而“超过”导航控制器的头部。 - matt
我还有一个问题,我们需要在 Info.plist 文件中添加这个 View controller-based status bar appearance 属性吗?先谢谢。@matt - MhmdRizk
@MhmdRizk 不是的。那已经过时了。视图控制器控制状态栏已经成为默认设置多年了。 - matt
自从这个解决方案能够正常工作以来,有什么改变或者出现了问题吗?我可以成功地在运行时更改导航栏样式(随意切换为.black和.default),但是我无法让状态栏改变其显示方式,以在黑色导航栏上显示浅色文本。我已经尝试了在SO上找到的所有解决方案,但都没有成功。你有什么其他建议吗? - randallmeadows

3

Swift 5, iOS 12

这里的绝大多数答案都很有帮助,但是对于我而言并没有立即解决问题。我有一个嵌套结构(根VC > 标签栏VC > 导航VC > 特定页面VC),我希望其中一个推出的页面VC可以改变状态栏颜色。

在页面VC中设置navigationBar.barStyle对我来说毫无作用,可能是因为导航VC不是顶级VC。在页面VC中设置preferredStatusBarStyle也不起作用(尽管如果我在根VC中覆盖此属性,它确实起作用)。

@kelin的回答指引了我正确的方向。 childForStatusBarStyle有助于指定应检查哪个子VC以获得适当的样式。以下是我采取的步骤:

步骤1:为我VC层次结构中的所有VC实现childForStatusBarHidden

例如对于根VC:

override var childForStatusBarStyle: UIViewController? {
  // this is a custom var I've set up
  return currentViewController
}

针对选项卡VC

override var childForStatusBarStyle: UIViewController? {
  return selectedViewController
}

为了导航视图控制器
override var childForStatusBarStyle: UIViewController? {
  return topViewController
}

步骤2: 确保正确调用childForStatusBarStyle函数。我的根视图控制器中的currentViewController变量在iOS检查之前没有被设置,所以我需要在该变量设置完毕后调用setNeedsStatusBarAppearanceUpdate()来指示再次检查childForStatusBarStyle函数。

步骤3: 在决定状态栏外观的子视图控制器中,覆盖样式:

override var preferredStatusBarStyle: UIStatusBarStyle {
  return .lightContent
}

步骤4: 确保在创建子VC时调用setNeedsStatusBarAppearanceUpdate()方法,并从根控制器中调用该方法。因此,子VC中的setNeedsStatusBarAppearanceUpdate()方法不起作用,但例如UIApplication.shared.keyWindow?.rootViewController?.setNeedsStatusBarAppearanceUpdate()可以。

步骤5: 当从层次结构中移除子VC并想要恢复状态栏样式时,请确保再次从根VC中调用setNeedsStatusBarAppearanceUpdate()方法,可能是在viewWillDisappear中。

顺便提一下,上述解决方案并不需要我明确设置View controller-based status bar appearance Info.plist值为YES,这是许多答案提到的。


1

对于UINavigationController,有一种替代方案。您可以对其进行子类化,并使用childForStatusBarHidden属性与setNeedsStatusBarAppearanceUpdate()相结合。

class StatusBarNavigationController: UINavigationController {
    override var childForStatusBarHidden: UIViewController? {
        return topViewController
    }

    override var viewControllers: [UIViewController] {
        didSet { setNeedsStatusBarAppearanceUpdate() }
    }
}

因此,状态栏样式将由topViewController定义。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接