码迷,mamicode.com
首页 > 移动开发 > 详细

iOS 使用Method Swizzling隐藏Status Bar

时间:2014-10-01 02:34:50      阅读:191      评论:0      收藏:0      [点我收藏+]

标签:des   style   blog   http   color   io   os   使用   ar   

在iOS 6中,隐藏Status Bar非常的简单。

// iOS 6及以前,隐藏状态栏
[[UIApplication sharedApplication] setStatusBarHidden:YES];


来到了iOS 7的年代以后,需要在UIViewController中指定:

#ifdef __IPHONE_7_0
- (BOOL)prefersStatusBarHidden {
    return YES;
}
#endif

并通过下列代码刷新状态栏:

if ([viewController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
        [viewController prefersStatusBarHidden];
        [viewController performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}


但是上述代码并不是万能的,iOS 7的某些场合还是会造成无法隐藏Status Bar的问题。

在ParentViewController中Add一个ChildViewController,如果ParentViewController的

prefersStatusBarHidden方法返回的是NO,那么即使ChildViewController中的prefersStatusBarHidden方法返回的是YES并调用以上代码,也无法隐藏Status Bar。


解决方案:Method Swizzling

在ChildViewController中Hook ParentViewController的prefersStatusBarHidden方法,使其返回YES,然后调用更新状态栏的代码,实现隐藏状态栏。需要注意的是,在适当场合,例如ChildViewController的viewWillDisappear方法中,需要将Hook的方法还原。否则可能造成奇怪的情况出现。


代码如下:

1.在ChildViewController的viewDidLoad方法中替换ParentViewController的prefersStatusBarHidden方法的实现

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _statusBarHidden = [UIApplication sharedApplication].statusBarHidden;
    // 进入界面时隐藏状态栏
    UIViewController *parentViewController = self.parentViewController;
    if ([parentViewController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
        [self hookPrefersStatusBarHidden:parentViewController];
    }
    else {
        // iOS 6及以前,隐藏状态栏
        [[UIApplication sharedApplication] setStatusBarHidden:YES];
    }
}

2.使用Runtime的Method Swizzling大法替换ChildViewController和ParentViewController两者的prefersStatusBarHidden方法的实现

- (void)hookPrefersStatusBarHidden:(UIViewController *)parentViewController {
    /**
     Method Swizzling
     
     1.如果ParentViewController的prefersStatusBarHidden返回NO,那么Add在其上的ChildViewController的prefersStatusBarHidden即使返回YES,也无法隐藏状态栏。因此在viewDidLoad时,需要将ParentViewController中prefersStatusBarHidden方法的实现替换掉
     2.在viewWillDisappear时,需要将交换的方法实现还原回来
     */
    Method src_method = class_getInstanceMethod([UIViewController class], @selector(prefersStatusBarHidden));
    Method des_method = class_getInstanceMethod([self class], @selector(hook_prefersStatusBarHidden));
    method_exchangeImplementations(src_method, des_method);
    
    // 刷新状态栏
    dispatch_async(dispatch_get_main_queue(), ^{
        [parentViewController prefersStatusBarHidden];
        [parentViewController performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
    });
}

- (BOOL)hook_prefersStatusBarHidden {
    // 隐藏状态栏
    return YES;
}

3.在ChildViewController从ParentViewController中移除时,viewWillDisappear方法必定会被调用(注意不要在viewDidDisappear方法中调用,此时ChildViewController可能已经被释放掉),因此可以在该方法中还原两者的prefersStatusBarHidden的实现

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    // 退出界面时,还原状态栏的初始状态
    UIViewController *parentViewController = self.parentViewController;
    if ([parentViewController respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
        [self hookPrefersStatusBarHidden:parentViewController];
    }
    else {
        // iOS 6及以前,恢复状态栏的初始状态
        [[UIApplication sharedApplication] setStatusBarHidden:_statusBarHidden];
    }
}


有时候为了确保状态栏隐藏,可以强制执行以上代码。

在实际工程中第二次用上Runtime的特性,实在开心,哈哈。


参考资料:

iOS7 隐藏状态栏 (电池栏)

Objective-C的hook方案(一): Method Swizzling



iOS 使用Method Swizzling隐藏Status Bar

标签:des   style   blog   http   color   io   os   使用   ar   

原文地址:http://blog.csdn.net/jymn_chen/article/details/39694147

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!