UINavigationController中适配UIStatusBarStyle

1,637 阅读3分钟

在页面结构使用UINavigationController时,针对不同的界面使用其自定义preferredStatusBarStyle属性的适配。

古老时代

open func setStatusBarStyle(_ statusBarStyle: UIStatusBarStyle, animated: Bool)

这个API是从iOS 2.0那个遥远的年代就已经存在的,通过它可以直接设置StatusBar的样式。不过现在都是iOS 13的新时代了,这个API已经在iOS 9之后就已经被Apple标记为deprecated了。

@available(iOS, introduced: 2.0, deprecated: 9.0, message: "Use -[UIViewController preferredStatusBarStyle]")
open func setStatusBarStyle(_ statusBarStyle: UIStatusBarStyle, animated: Bool)

当然了如果你喜欢,你也可以继续使用它。不过前提是你必须在Info.plist中增加一个View controller-based status bar appearance的配置项,值为NO。默认情况下这个配置项是没有的,默认值为YES。然后你就可以愉快的玩耍了(此次应该有👏)。

如果你是个强迫症患者,面对满屏的deprecated警告,这样你还能忍(灵魂拷问)?当然了,你说我不在乎,拿起键盘就是干!那么恭喜你,为以后埋下了一颗编译级别的雷(😏)。别人下班出去约会妹子,而你还在一个人与bug为伴(凄凄惨惨)。

如何拯救

其实在iOS 7时代,苹果通过限制改变StatusBar的自由性,进而把控制权交给了UIViewController,每个界面都应该负责所属自己StatusBar的样式。这样一来,事情就变的简单多了。在UIViewController中重写preferredStatusBarStyle属性就能定义自己界面的StatusBar样式了。

override var preferredStatusBarStyle: UIStatusBarStyle { return .default }

在应用运行时,由当前所展现的ViewController来决定StatusBar的样式。如果你没有重写此属性,那么默认值为default。但是,如果当前ViewController是由UINavigationController管理的时候,你会发现无论你如何更改此属性都不会有效果(心里默念mmp)。

废话不多说,直接上代码。下面这段代码,是将StatusBar的样式交由当前UINavigationController的栈顶控制器负责。如果topViewController没有重写preferredStatusBarStyle属性,那么默认值就是default

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

这个方案只适合自定义UINavigationController的时候。如果你没有自定义,直接使用UINavigationController,那么你也可以通过UINavigationBarbarStyle属性,来改变StatusBar的样式。这个不在本文讨论范围之内,不再细说。

例外

如果你使用present方式来展现一个UINavigationController时,你会发现上述代码会有一点小问题。既不会使用当前present出来的ViewController的preferredStatusBarStyle。当然这种情况也必须是你自定义的UINavigationController,咱们直接上代码。

open override var preferredStatusBarStyle: UIStatusBarStyle {
    if let presentedViewController = self.presentedViewController, let vc = self.visibleViewController, !presentedViewController.isBeingDismissed {
        return vc.preferredStatusBarStyle
    }
    return self.topViewController?.preferredStatusBarStyle ?? .default
}

只需要加一个判断条件,如果当前UINavigationController是使用present形式展现的,判断当前状态是Dismissed还是Presented。
如果是Presented状态,可以使用visibleViewController这个属性获取当前modal出来的ViewController,使用其preferredStatusBarStyle来管理StatusBar的样式。
如果是Dismissed状态,那使用topViewController的preferredStatusBarStyle来管理StatusBar的样式。

总结

如果使用自定义UINavigationController其使用present方式,需要visibleViewController+topViewController两者结合的方式适配。
如果只是单纯的使用自定义UINavigationController,只需要使用topViewController属性的方式适配。
如果你没有使用自定义UINavigationController,那么可以考虑使用UINavigationBarbarStyle属性的方式适配。

转载自我的博客