http://www.cocoachina.com/ios/20160718/17020.html

背景

iOS 中经常会有需要在某个界面改变状态栏颜色或者某个界面隐藏状态栏的需求。而改变状态栏颜色和控制状态栏显示和隐藏的API,在iOS 的不同版本中也发生了很多变化。

iOS 7以前

在iOS 7之前,状态栏是不占视图位置的。每个控制器中的根view都是从屏幕的Y轴20px处开始显示的。所以那个时候整个app状态栏的风格,一般只在plist文件里设置【对应于General中的Status Bar Style】。印象里似乎只有黑白两种风格,已记不清了!

iOS 7以前状态栏设置

从API来看,那时候也是支持在代码里修改状态栏的样式以及显示和隐藏的。只是因为状态栏对整个APP的影响不大,所以一般在plist里设置好后,用不着再去修改了。

API

iOS 7 ~iOS 9

从iOS 7开始系统风格大变样,图标扁平了,状态栏也不在闹独立了。因为状态栏的会受到导航栏或者View背景色的影响,所以状态栏的风格也需要实时调整了。

想要改变状态栏的样式,想要控制状态栏的显示与隐藏,该怎么做呢?

1. 用UIApplication的API

首先,需要在plist文件里将【View controller-based status bar appearance】设置为NO,因为它的默认值是YES,然后就可以利用UIApplication 来设置了。

plist设置

先上效果动画:

再上源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (IBAction)changeStatus:(UISegmentedControl *)sender {
    if (sender.selectedSegmentIndex == 0) {
//        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault];
        // 带动画效果,动画效果其实就是变换的时间变慢了
        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
    else {
//        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
        // 带动画效果,动画效果其实就是变换的时间变慢了
        [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
    }
}
- (IBAction)showOrHidden:(UISegmentedControl *)sender {
    if (sender.selectedSegmentIndex == 0) {
        // 第二个参数是个枚举类型
        [[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
        // 不带动画效果
//        [[UIApplication sharedApplication] setStatusBarHidden:NO];
    else {
        [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationSlide];
        // 不带动画效果
//        [[UIApplication sharedApplication] setStatusBarHidden:YES];
    }
}

2. 重写ViewController方法

首先,要确保plist文件中【View controller-based status bar appearance】为YES,没有添加这个key的时候,默认是YES。

plist设置

然后在视图控制器中,重写如下三个方法即可:

要重写的方法

因为这三个方法都有默认值,如果我们要的状体栏样式什么的跟默认值效果一致,则不需要重写;如果不想要默认的效果,则直接在这三个方法里return 相应的值即可。你不必三个方法都重写,看实际情况。例如,我想要在这个界面时状态栏为白色,状态栏不隐藏,那么我只用重写-preferredStatusBarStyle,like this:

1
2
3
4
- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}

因为我这里需要做一个切换所以,我首先定义了两个property:

1
2
@property (assign, nonatomic)   UIStatusBarStyle            statusBarStyle;  /**< 状态栏样式 */
@property (assign, nonatomic)   BOOL                        statusBarHidden;  /**< 状态栏隐藏 */

然后改变UISegmentedControl的值时,在响应的Action方法里改变上述property的值,再调用

-setNeedsStatusBarAppearanceUpdate即可。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#pragma mark - ViewController方式
- (IBAction)changeStyle:(UISegmentedControl *)sender {
    if (sender.selectedSegmentIndex == 0) {
        _statusBarStyle = UIStatusBarStyleDefault;
    else {
        _statusBarStyle = UIStatusBarStyleLightContent;
    }
    [self setNeedsStatusBarAppearanceUpdate];
}
- (IBAction)statusShowOrHidden:(UISegmentedControl *)sender {
    if (sender.selectedSegmentIndex == 0) {
        _statusBarHidden = NO;
    else {
        _statusBarHidden = YES;
    }
    [self setNeedsStatusBarAppearanceUpdate];
}
#pragma mark - 需要重写的几个状态栏方法
/**
 *  控制状态栏的样式
 *  要刷新状态栏,让其重新执行该方法需要调用{-setNeedsStatusBarAppearanceUpdate}
 *
 *  @return 将要显示的状态栏样式
 */
- (UIStatusBarStyle)preferredStatusBarStyle
{
    return _statusBarStyle;
}
/**
 *  状态栏显示还是隐藏
 *  要刷新状态栏,让其重新执行该方法需要调用{-setNeedsStatusBarAppearanceUpdate}
 *
 *  @return BOOL值
 */
- (BOOL)prefersStatusBarHidden
{
    return _statusBarHidden;
}
/**
 *  状态栏改变的动画,这个动画只影响状态栏的显示和隐藏
 *
 *  @return 动画效果
 */
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
    return UIStatusBarAnimationSlide;
}

iOS 9 之后

如上面第二张图所示,UIApplication的控制状态栏的方法,在iOS 9之后被弃用了。

所以iOS 9之后尽量使用重写ViewController方法的方式吧。

注意点

  • 情形一

如果我们使用UINavigationController,会发现在原来的ViewController里修改状态栏的style不起作用了,但是控制状态栏的显示和隐藏依然OK。但是使用UITabBarController依然正常,状态栏不受UITabBarController影响。

重写UINavigationController的三个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (UIStatusBarStyle)preferredStatusBarStyle
{
    NSLog(@"导航栏-%s",__func__);
    return [self.topViewController preferredStatusBarStyle];
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
    NSLog(@"导航栏-%s",__func__);
    return UIStatusBarAnimationNone;
}
- (BOOL)prefersStatusBarHidden
{
    NSLog(@"导航栏-%s",__func__);
    return NO;
}

从打印结果:

1
2
3
4
5
6
2016-05-18 13:18:10.248 PractiseProject[3296:112707] 导航栏--[BaseNavigationController preferredStatusBarStyle]
2016-05-18 13:18:10.249 PractiseProject[3296:112707] -[ViewController prefersStatusBarHidden]
2016-05-18 13:18:10.275 PractiseProject[3296:112707] 导航栏--[BaseNavigationController preferredStatusBarStyle]
2016-05-18 13:18:10.275 PractiseProject[3296:112707] -[ViewController prefersStatusBarHidden]
2016-05-18 13:18:10.275 PractiseProject[3296:112707] 导航栏--[BaseNavigationController preferredStatusBarStyle]
2016-05-18 13:18:10.276 PractiseProject[3296:112707] -[ViewController prefersStatusBarHidden]

可以看出,只调用了第一个方法。所以我们只需要重写UINavigaitonController的- preferredStatusBarStyle即可。

  • 情形二

状态栏的样式、是否显示实际上是由顶层window的当前视图控制器决定的。

比如我们在程序入口处创建一个新的window:

1
2
3
4
5
6
7
8
9
10
11
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    self.statusWindow = [[UIWindow alloc] initWithFrame:application.statusBarFrame];
    // 这里设置windowLevel 为UIWindowLevelStatusBar或者UIWindowLevelAlert都可以
    self.statusWindow.windowLevel = UIWindowLevelStatusBar;
    // 颜色必须为clearColor,否则会盖住状态栏的区域
    self.statusWindow.backgroundColor = [UIColor clearColor];
    self.statusWindow.rootViewController = [[StatusViewContrller alloc] init];
    self.statusWindow.hidden = NO;
    return YES;
}

然后在StatusViewContrller中重写如下方法:

1
2
3
4
5
6
7
8
9
- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}
// 如果想要显示状态栏,必须重写这个方法,并return NO
- (BOOL)prefersStatusBarHidden
{
    return NO;
}

这样最终状态栏的样式就由StatusViewContrller决定了,而不是由原来的ViewController决定了。

创建顶层window之后,修改状态栏的样式就不方便了。

为了解决这个问题,我们可以将StatusViewContrller弄成单例,然后定义两个property来控制样式和是否隐藏即可。

1
2
3
4
5
6
#import <uikit uikit.h="">
@interface StatusViewContrller : UIViewController
@property (assign, nonatomic)   UIStatusBarStyle            statusBarStyle;  /**< 状态栏样式 */
@property (assign, nonatomic)   BOOL                        statusBarHidden;  /**< 状态栏隐藏 */
+ (instancetype)sharedInstance;
@end</uikit>

重写两个property的set方法,设置完属性后调用状态栏刷新方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 创建单例的关键代码
static id instance = nil;
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}
// 这个方法是关键
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}
// 重写的方法
- (UIStatusBarStyle)preferredStatusBarStyle
{
    return _statusBarStyle;
}
- (BOOL)prefersStatusBarHidden
{
    return _statusBarHidden;
}
// setter
- (void)setStatusBarStyle:(UIStatusBarStyle)statusBarStyle
{
    _statusBarStyle = statusBarStyle;
    [self setNeedsStatusBarAppearanceUpdate];
}
- (void)setStatusBarHidden:(BOOL)statusBarHidden
{
    _statusBarHidden = statusBarHidden;
    [self setNeedsStatusBarAppearanceUpdate];
}

创建了顶层window后,唯一需要注意的是顶层window和其根视图控制器的背景色必须为clearColor。

iOS Status Bar变换的更多相关文章

  1. IOS status bar

    从iOS7开始,该系统提供2样的管理风格状态栏 由UIViewController管理(每UIViewController我们可以有各自不同的状态栏) 由UIApplication管理(由其统一管理的 ...

  2. iOS 使用Method Swizzling隐藏Status Bar

    在iOS 6中,隐藏Status Bar很的简单. // iOS 6及曾经,隐藏状态栏 [[UIApplication sharedApplication] setStatusBarHidden:YE ...

  3. iOS开发-UINavigationBar和Status Bar实用技巧

    iOS7之后关于UINavigationBar和Status  Bar都发生了一系列的改变,如果不需要兼容iOS7之后的设备,按照网上有些资料去解决问题会踩到一些坑.在iOS 7中,我们可以修改每个V ...

  4. Status bar and navigation bar appear over my view's bounds in iOS 7

    转自:http://stackoverflow.com/questions/17074365/status-bar-and-navigation-bar-appear-over-my-views-bo ...

  5. 转 如何在IOS设备中去掉屏幕上的status bar

    引入如何在IOS设备中去掉屏幕上的status bar,即:不显示设备上方的[网络.时间.电池??]条?操作方法一:在-info.list项目文件中,加上“Status bar is initiall ...

  6. iOS 7 改变Status Bar 颜色

    Set the UIViewControllerBasedStatusBarAppearance to NO in the Info.plist. In ViewDidLoad method or a ...

  7. Status bar - iOS之状态栏

    (一)设置状态栏显示和隐藏 1.通过 Info.plist 文件增加字段,控制状态栏全局显示和隐藏 在 Info.plist 文件中增加字段 Status bar is initially hidde ...

  8. Customizing Navigation Bar and Status Bar

    Like many of you, I have been very busy upgrading my apps to make them fit for iOS 7. The latest ver ...

  9. Status Bar in iOS7

    This is a very important change in iOS 7: the status bar is no longer a separate bar. It’s now somet ...

随机推荐

  1. sql server2008安装时提示重启计算机失败怎么办

    安装SQL Server 2008时,经常会遇到这样一个问题,软件提示“重启计算机失败”,如果忽略的话,会给后面的安装带来很大的麻烦,这里如何解决呢?   工具/原料 注册表 解决方法   在键盘上按 ...

  2. 分布式配置中心(Spring Cloud Config)

    真有意思的一个问题,我先把我遇到的写一次 ,今天学习Spring Cloud Config  新建了三个module ,eureka-server,config-server,config-clien ...

  3. 常用es6特性归纳-(一般用这些就够了)

    之前做vue和react的时候,发现文档什么的最新版本都建议用es6写法,对es6友好度更高,加之现在es6也越来越普及,兼容性问题直接用babel转码就好了,特别方便,于是我开始学着用es6写代码, ...

  4. Leetcode139. Word Break单词拆分

    给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词. 说明: 拆分时可以重复使用字典中的单词. 你可以假设字典中没有重复 ...

  5. python 3.6 关于python的介绍

    python的官方网站 https://www.python.org/ python 3.6 的官方网站的下载地址 https://www.python.org/downloads/release/p ...

  6. 洛谷P2333 [SCOI2006]一孔之见

    传送门 辣鸡题目毁我人生败我前程 50分代码 //Achen #include<algorithm> #include<iostream> #include<cstrin ...

  7. Putty保存设置

    使用putty连接服务器每次都要配置编码,有时候忘了配置,提示就出现中文乱码真是坑爹(纯英文提示也行啊),好了闲话少说,步入正题. Putty的设置保存功能隐藏的实在太好了,原来在Connection ...

  8. jeecms 链接标签

    .引入页面 [#include "../include/header-site.html"/]12.导航栏只有前两个带链接 [#if c_index<2] href=&quo ...

  9. SpringMVC配置顺序的问题

    1:web.xml:web应用一经加载,先来找他         1):指明applicationContext的位置         2):引入spring监听,ContextLoaderListe ...

  10. netbeans 8.2 系统找不到指定的文件。

    系统找不到指定的文件.Using CATALINA_BASE: "C:\Users\wishr\AppData\Roaming\NetBeans\8.2\apache-tomcat-8.0. ...