最近老板有個需求,需要把項目中所有的線條都給去掉,包括導航欄上的那根黑線,本來這個需求也很正常.可是我們的項目時間比較久遠了,所以有的控制器繼承了基類,而有的則沒有,難道我需要在每個控制器里面都寫一遍隱藏的代碼么?特別是在一個需求多變的公司,假如哪次老板說需要這條線了,那我得重來一次么.不過我不不太喜歡寫重復性的代碼,所以就開始著手解決這件事了.
第一種:
由于1px的黑色線條是個黑色陰影圖片,所以我們可以通過設置這張圖片去讓它隱藏或者更換顏色.
①全局設置
//兩句話需要配合使用才有效
[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"white.jpg"] forBarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setShadowImage:[UIImage new]];
這是一種比較官方的一種寫法,不過這種方法會修改navigationBar的translucent(半透明)屬性,這個屬性會影響到我們的界面布局.
接下來我通過一個demo去驗證它,我創(chuàng)建了兩個ViewController,分別對應兩個UINavigationController.FirstVC采用了StoryBoard生成的界面,而ThirdVC則是通過代碼生成的,當我們在AppDelegate.m中使用了上述方法后:



從圖片我們可以發(fā)現,使用了修改圖片來達到隱藏線條的目的是完全可行的.但是,在SB中可能不會有太 大影響,不過在非SB的布局中,我們發(fā)現view的渲染是從導航欄的底部開始的,也是說View的Y點為64,所以說在該視圖上的控件的Frame也會改變.如果說我們在該界面有一個隱藏和顯示導航欄的操作,出現的后果則是:
//導航欄顯示下的Frame
self.view.frame = CGRectMake(0, 64, 375, 603);
//導航欄隱藏下的Frame
self.view.frame = CGRectMake(0, 0, 375, 667);
不過既然是問題,那就有解決的辦法,我們可以通過設置translucent為YES就可以解決了,這時候View的布局會充滿全屏.
②單頁面設置
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"white.jpg"] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[UIImage new]];
}
-(void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"white.jpg"] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:nil];
}
第二種:
由于第一種的方法讓我項目中的VC出現了各種各樣的問題,而我又不想一個個去改,所以又有了接下來的辦法.我們可以通過遍歷導航欄的子控件,尋找到這個黑色線條,在合適的時候隱藏或者顯示,這時候我們就不會改變UINavigationBar的translucent了,也不會影響我們視圖的布局.
①單頁面設置
在viewWillAppear:或者viewWillDisappear:中調用hideNavigationBarBottomLine:方法就可以完成對線條的隱藏或顯示.
-(void)hideNavigationBarBottomLine:(BOOL)hidden{
//iOS10及以上的隱藏方法
if ([UIDevice currentDevice].systemVersion.floatValue >= 10.0) {
if ([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) {
NSArray *list=self.navigationController.navigationBar.subviews;
for (id obj in list) {
//10.0的系統(tǒng)字段不一樣
UIView *view = (UIView*)obj;
for (id obj2 in view.subviews) {
if ([obj2 isKindOfClass:[UIImageView class]]) {
UIImageView *image = (UIImageView*)obj2;
if (image.frame.size.height <= 1) {
image.hidden = hidden;
}
}
if ([obj2 isKindOfClass:[UIView class]]) {
UIView *view2 = (UIView *)obj2;
if (view2.frame.size.height <= 1) {
view2.hidden = hidden;
}
}
}
}
}
return;
}
//iOS10以下的隱藏方法
if ([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]){
NSArray *list = self.navigationController.navigationBar.subviews;
for (id obj in list) {
if ([obj isKindOfClass:[UIImageView class]]) {
UIImageView *imageView = (UIImageView *)obj;
NSArray *list2 = imageView.subviews;
for (id obj2 in list2) {
if ([obj2 isKindOfClass:[UIImageView class]]) {
UIImageView *imageView2 = (UIImageView *)obj2;
if (imageView2.frame.size.height <= 1) {
imageView2.hidden = hidden;
}
}
if ([obj2 isKindOfClass:[UIView class]]) {
UIView *view2 = (UIView *)obj2;
if (view2.frame.size.height <= 1) {
view2.hidden = hidden;
}
}
}
}
}
}
}
②最終方案(全局設置)
but,之前提過,因為不想寫重復性的代碼,所以才來折騰的,當時就想,如果項目中所有的類都繼承了基類該多好啊,在基類的viewWillAppear:或者viewWillDisappear:中改一次就好了.想到這里,我突然想起來有一個方法可以每次都能調用這些方法,那就是runtime.我們可以寫一個UIViewController的類別,通過runtime我們可以獲取到UIViewController生命周期方法,在viewDidLoad中,我們只用寫一次代碼就可以了,后續(xù)的視圖控制器只要在生命周期中,都會到類別中去掉用隱藏的方法,方便又快捷,不要寫大量重復性的代碼.并且哪次老板需要顯示線條的時候,只要把這個類別移除就可以了,不用涉及到項目中其他類的操作.對于oc運行時特性不了解的自行百度,具體的實現思路如下:
通過重寫NSObject類都有的Load方法,使用runtime替換系統(tǒng)的viewDidLoad方法為自己的hide_viewDidLoad方法.
+(void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector1 = @selector(viewDidLoad);
SEL swizzledSelector1 = @selector(hide_viewDidLoad);
Method originalMethod1 = class_getInstanceMethod(class, originalSelector1);
Method swizzledMethod1 = class_getInstanceMethod(class, swizzledSelector1);
BOOL didAddMethod =
class_addMethod(class,
originalSelector1,
method_getImplementation(swizzledMethod1),
method_getTypeEncoding(swizzledMethod1));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector1,
method_getImplementation(originalMethod1),
method_getTypeEncoding(originalMethod1));
} else {
method_exchangeImplementations(originalMethod1, swizzledMethod1);
}
});
}
在替換的方法中寫我們相應的需求:
-(void)hide_viewDidLoad {
[self hide_viewDidLoad];
if (self.navigationController) {
[self hideNavigationBarBottomLine:YES];
}
}