关于 StatusBar 与 NavigationBar 适配的一些坑
最近在项目中遇到这样一个坑:项目中
NavigationController
之下的ViewControllers
的NavigationBar
有不同的样式(纯色Translucent,完全透明、隐藏等样式)。由于NavigationBar
的颜色不同,StatusBar
的颜色也必须随着其颜色深浅来作出适配。
适配StatusBar
- (UIStatusBarStyle)preferredStatusBarStyle
方法的半残废,在NavigationController
下的ViewControllers
直接使用该方法是不会执行的,因为NavigationController
的特殊性,同时在查阅Apple官方文档可以重写childViewControllerForStatusBarStyle
这个方法:
但是太麻烦了,并没有什么卵用。
所以,我们可以一劳永逸,写一个UINavigationController
的Category来扩展此方法:
#import <UIKit/UIKit.h>
@interface UINavigationController (GBExtension)
- (UIViewController *)childViewControllerForStatusBarStyle;
- (UIViewController *)childViewControllerForStatusBarHidden;
@end
#import "UINavigationController+StatusBarStyle.m"
@implementation UINavigationController (StatusBarStyle)
- (UIViewController *)childViewControllerForStatusBarStyle {
return self.topViewController;
}
- (UIViewController *)childViewControllerForStatusBarHidden {
return self.topViewController;
}
@end
最重要的一步,在Info.plist
文件中若有UIViewControllerBasedStatusBarAppearance
这个键值对务必删除掉,因为在iOS9中已不建议使用改方式。
最后,在需要改变StatusBarStyle
的ViewController
中重写preferredStatusBarStyle
方法即可生效,如果不重写,则为默认的StatusBarStyle
,在项目设置中可以找到该选项:
适配NavigationBar
前面说完了StatusBarStyle
适配的坑,轮到了NavigationBar
,由于各种各样的花式ViewControllers
中要有不同的NavigationBar
样式,有下面的各种方法与其利弊:
最笨的方法就是在每个
ViewController
中加入设置NavigationBar
样式的代码- 优点:人人都会。
- 缺点:工作量十分巨大,产品经历一个心血来潮说要改,分分钟弄死程序员。
写一个
BaseViewConroller
并实现自定义NavigationBar
样式的方法,所有的ViewController
都将继承于BaseViewController
- 优点:比较符合
Don't repeat your self
。 - 缺点:离开了
BaseViewController
之后会显得非常无助,而且作为整个项目中举足轻重的基类BaseViewController
,很有可能会写得很庞大臃肿,到最后谁都不敢动。
- 优点:比较符合
那么既要降低耦合又要实现效果,不采用继承,那么就组合方式比较合适,因此我们可以采用iOS中强大的
Runtime
机制,用Method Swizzling
这个黑科技来无痛嵌入到项目当中,实现效果。
关于Method Swizzling
,安利一下一个很好用的第三方库:Aspects。我们可以不用写在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
这个鬼地方,避免代码臃肿,我们可以直接在NSObject
的+ (void)load
方法中进行Swizzle:
#import <Foundation/Foundation.h>
#import <Aspects/Aspects.h>
@interface ViewControllerIntercepter : NSObject
@end
#import "GBViewControllerIntercepter.h"
@implementation ViewControllerIntercepter
#pragma mark - LifeCycle
+ (void)load {
[super load];
[UIViewController aspect_hookSelector:@selector(viewWillAppear:)
withOptions:AspectPositionAfter
usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated){
[self viewWillAppear:animated viewController:[aspectInfo instance]];
} error:NULL];
[UIViewController aspect_hookSelector:@selector(viewWillDisappear:)
withOptions:AspectPositionAfter
usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated){
[self viewWillDisappear:animated viewController:[aspectInfo instance]];
} error:NULL];
[UIViewController aspect_hookSelector:@selector(viewDidAppear:)
withOptions:AspectPositionAfter
usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated){
[self viewDidAppear:animated viewController:[aspectInfo instance]];
} error:NULL];
[UIViewController aspect_hookSelector:@selector(viewDidDisappear:)
withOptions:AspectPositionAfter
usingBlock:^(id<AspectInfo> aspectInfo, BOOL animated){
[self viewDidDisappear:animated viewController:[aspectInfo instance]];
} error:NULL];
}
#pragma mark Intercepter method
+ (void)viewWillAppear:(BOOL)animated viewController:(UIViewController *)viewController {
//Do anything you want
}
+ (void)viewWillDisappear:(BOOL)animated viewController:(UIViewController *)viewController {
//Do anything you want
}
+ (void)viewDidAppear:(BOOL)animated viewController:(UIViewController *)viewController {
//Do anything you want
}
+ (void)viewDidDisappear:(BOOL)animated viewController:(UIViewController *)viewController {
//Do anything you want
}
@end
使用Method Swizzling
在ViewController
的appear和disapear方法中注入自己的方法之后,我们就可以再扩展一下ViewController
加入一些返回自定义NavigationBar
样式属性的方法,然后加入到Swizzle
的方法里面,就可以无痛嵌入项目同时实现效果,这里我就暂时不贴代码啦,即使移除了该Method Swizzling
,项目依旧可以正常运行(需要实现的效果不再生效而已)
使用Method Swizzling
的另外一个好处就是当项目需要加入页面统计分析等功能(如腾讯MTA,友盟等),同样可以无痛嵌入到项目当中去,真是酸爽~