天天看點

狀态欄Status Bar變換

iOS 7 ~iOS 9

從iOS 7開始系統風格大變樣,圖示扁平了,狀态欄也不在鬧獨立了。因為狀态欄的會受到導航欄或者View背景色的影響,是以狀态欄的風格也需要實時調整了。

想要改變狀态欄的樣式,想要控制狀态欄的顯示與隐藏,該怎麼做呢?

1. 用UIApplication的API

首先,需要在plist檔案裡将【View controller-based status bar appearance】設定為NO,因為它的預設值是YES,然後就可以利用UIApplication 來設定了。

狀态欄Status Bar變換

plist設定

先上效果動畫:

狀态欄Status Bar變換

再上源碼:

- (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。

狀态欄Status Bar變換

plist設定

然後在視圖控制器中,重寫如下三個方法即可:

狀态欄Status Bar變換

要重寫的方法

因為這三個方法都有預設值,如果我們要的狀體欄樣式什麼的跟預設值效果一緻,則不需要重寫;如果不想要預設的效果,則直接在這三個方法裡return 相應的值即可。你不必三個方法都重寫,看實際情況。例如,我想要在這個界面時狀态欄為白色,狀态欄不隐藏,那麼我隻用重寫-preferredStatusBarStyle,like this:

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}      

因為我這裡需要做一個切換是以,我首先定義了兩個property:

@property (assign, nonatomic)   UIStatusBarStyle            statusBarStyle;  /**< 狀态欄樣式 */
@property (assign, nonatomic)   BOOL                        statusBarHidden;  /**< 狀态欄隐藏 */      

然後改變UISegmentedControl的值時,在響應的Action方法裡改變上述property的值,再調用

-setNeedsStatusBarAppearanceUpdate即可。

示例代碼:

#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;
}      
狀态欄Status Bar變換

iOS 9 之後

如上面第二張圖所示,UIApplication的控制狀态欄的方法,在iOS 9之後被棄用了。

是以iOS 9之後盡量使用重寫ViewController方法的方式吧。

注意點

  • 情形一

如果我們使用UINavigationController,會發現在原來的ViewController裡修改狀态欄的style不起作用了,但是控制狀态欄的顯示和隐藏依然OK。但是使用UITabBarController依然正常,狀态欄不受UITabBarController影響。

重寫UINavigationController的三個方法:

- (UIStatusBarStyle)preferredStatusBarStyle
{
    NSLog(@"導航欄-%s",__func__);
    return [self.topViewController preferredStatusBarStyle];
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
{
    NSLog(@"導航欄-%s",__func__);
    return UIStatusBarAnimationNone;
}
- (BOOL)prefersStatusBarHidden
{
    NSLog(@"導航欄-%s",__func__);
    return NO;
}      

從列印結果:

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:

- (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中重寫如下方法:

- (UIStatusBarStyle)preferredStatusBarStyle
{
    return UIStatusBarStyleLightContent;
}
// 如果想要顯示狀态欄,必須重寫這個方法,并return NO
- (BOOL)prefersStatusBarHidden
{
    return NO;
}      

這樣最終狀态欄的樣式就由StatusViewContrller決定了,而不是由原來的ViewController決定了。

建立頂層window之後,修改狀态欄的樣式就不友善了。

為了解決這個問題,我們可以将StatusViewContrller弄成單例,然後定義兩個property來控制樣式和是否隐藏即可。

#import 
@interface StatusViewContrller : UIViewController
@property (assign, nonatomic)   UIStatusBarStyle            statusBarStyle;  /**< 狀态欄樣式 */
@property (assign, nonatomic)   BOOL                        statusBarHidden;  /**< 狀态欄隐藏 */
+ (instancetype)sharedInstance;
@end      
// 建立單例的關鍵代碼
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];
}      

繼續閱讀