在iOS应用程序开发中,状态栏是显示在屏幕顶部的系统信息条,用于显示信号、电量等重要信息。有时候我们需要对状态栏颜色进行自定义,但在实际开发中可能会遇到一些困难。
之前写过iOS状态栏如何设置的问题,见链接 iOS状态栏设置 - 简书 (jianshu.com)
我们用的方案是 控制器优先控制状态栏的展示,也就是默认的,状态栏的文字是黑色的。在需要更新状态栏颜色的控制器返回需要的样式,比如在视频播放器页面状态栏文字返回白色。
问题描述
但是最近遇到了状态栏不更新的问题。问题是这样的,在使用模态方式(模态风格是custom)弹出了带导航栏的控制器后,使用该控制器,push 一个需要展示 白色文字 状态栏的 视图控制器,这个控制器关于状态栏的方法一直未被调用,导致使用的还是黑色文字的状态栏。
就是 图中 这个导航 push 出来的页面控制器 状态栏相关的方法都没有被调用。导致状态栏没有更新为预期的样子。
问题分析
而不是这种present出来的导航push就没有这个问题。更严格的说,模态方式是 UIModalPresentationFullScreen 这种方式 也是没有问题的。但问题使用其他的方式 比如 UIModalPresentationCustom 或者 UIModalPresentationOverFullScreen 或者 UIModalPresentationOverCurrentContext 会有这个问题。这说明模态方式,也会影响状态栏的设置。
而我们的业务需要这个present出来的页面是半透明的,因此不能用UIModalPresentationFullScreen,只能用 UIModalPresentationCustom 或者 UIModalPresentationOverFullScreen,使用这种方式,状态栏就是不能正确更新。
我们无法更改 UIModalPresentationStyle ,只能 想办法 让 我们viewController状态栏相关的方法得到调用。
问题处理
第一步: 打通状态栏样式的传递链条
以视图控制器优先控制状态栏的方式中,window的状态管理器会层层询问,如果问到了就不再询问,比如会问window的root VC,状态栏要展示什么,root VC 如果返回了就不再继续,通常 rootVC是个容器控制器,会询问子控制器,一直问到结果位置,因此,要想 子控制的状态栏相关的方法会被调用,这个链条得通。所以要对 容器做处理,实现状态栏相关的询问方法。
比如,对于Tabbar控制器,我们这么写
- (UIViewController *)childViewControllerForStatusBarHidden {
return self.selectedViewController;
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return self.selectedViewController;
}
对于导航控制器,我们这么写
- (UIViewController *)childViewControllerForStatusBarHidden {
return self.topViewController;
}
- (UIViewController *)childViewControllerForStatusBarStyle {
return self.topViewController;
}
第二步: 模态时接管状态栏的处理
通过第一步,我们保证了即便控制器被包括在容器中,也都有机会处理状态栏。
然而,这么写,仍然没有解决我们的问题,我们的方法没有被调用,因为图中那个导航控制器相关的询问事件都没有触发。看似是系统导致的,我们很难找到原因,我们只知道模特的方法影响了状态栏响应链条的传递。
经过仔细的分析,不停的试一试,我们终于找到了问题的关键。当我们以模态方式出现时,并且有些模态方式,控制器是拿不到状态栏的掌控权的,其中有一个属性就是用来做这个事情的。这个属性就是 modalPresentationCapturesStatusBarAppearance
该属性的默认值是NO,也就是说 以模态方式出现时,默认不 捕获状态栏的 处理。
然而,我们肯定是要接管状态栏的处理的,因为必须将这个属性设置为YES,可以通过复写 也可以直接设置。
在我们的问题中,我们只需要复写或者设置这个属性,一定接管 状态栏的处理,我们的问题就得到了解决。
- (BOOL)modalPresentationCapturesStatusBarAppearance {
return YES;
}
通过以上两个步骤,让我们的应用彻底掌握状态栏的设置。