對于iOS 開發(fā)者來說,導(dǎo)航欄確實(shí)是一個讓人困惑的知識點(diǎn)。比如設(shè)置導(dǎo)航欄透明效果,透明導(dǎo)航欄與非透明導(dǎo)航欄之間的跳轉(zhuǎn)等等,我開始也是在項(xiàng)目預(yù)定的框架 下去設(shè)置導(dǎo)航欄的一些屬性,直到我負(fù)責(zé)的模塊在IOS10中出現(xiàn)了導(dǎo)航欄的bug的時候才不得不 去好好消化這一塊,去理解系統(tǒng)在導(dǎo)航切換時的一些特效,去調(diào)研市面上常用APP對于導(dǎo)航欄 的一些處理,這里我就來分解一下導(dǎo)航欄的奧秘。
導(dǎo)航欄基礎(chǔ)
iOS 導(dǎo)航欄大致有兩部分組成:navigationBar和navigationItem:
- navigationBar
navigationBar是navigationController的一部分,它不屬于單個的UIViewController。我們在同一個navigationController中push或者pop的時候,看到的bar其實(shí)都是一個,也就是說,在一個viewController中修改了bar的屬性之后在其他的控制器中也是可見的。
在考慮設(shè)置導(dǎo)航欄的時候,我們通常也是一起設(shè)置了App的狀態(tài)欄(也就是電池條),IOS有兩個常用的 狀態(tài)欄(statusBarStyle)屬性,分別是:
在考慮設(shè)置導(dǎo)航欄的時候,我們通常也是一起設(shè)置了App的狀態(tài)欄(也就是電池條),IOS有兩個常用的 狀態(tài)欄(statusBarStyle)屬性,分別是:
// Dark content, for use on light backgrounds
UIStatusBarStyleDefault = 0,
// Light content, for use on dark backgrounds
UIStatusBarStyleLightContent NS_ENUM_AVAILABLE_IOS(7_0) = 1,
導(dǎo)航欄始終處于狀態(tài)欄的下方,我們可以看到幾乎所有的App導(dǎo)航欄和狀態(tài)欄的顏色 都相同,這樣的效果無法通過設(shè)置導(dǎo)航欄的backgroundColor得到,因?yàn)閷?dǎo)航欄y坐標(biāo)為20, 狀態(tài)欄,設(shè)置了導(dǎo)航欄的背景色不會影響到狀態(tài)欄。但是神奇的是,導(dǎo)航欄上面還有一層 View是navigationBarBackground(設(shè)置barTintColor將改變此背景色),其y坐標(biāo)為-20,剛好把狀態(tài)欄覆蓋,所以我們使用setBackgroundImage就可以保證導(dǎo)航欄與狀態(tài)欄同色,如下:
UIImage *colorImage = [UIImage imageWithColor:[UIColor clearColor] size:CGSizeMake(1, 1)];
[navc.navigationBar setBackgroundImage:colorImage forBarMetrics:UIBarMetricsDefault];
[navc.navigationBar setShadowImage:colorImage];
navigationItem
我們強(qiáng)調(diào)了navigationBar是屬于navigationController的一部分,那么navigationItem 卻是屬于UIViewCOntroller的一部分,這一點(diǎn)在剛開始很容易造成困惑,這些個Item明明 就在Bar上,為什么偏偏屬于ViewController,但是事實(shí)就是這樣。navigationItem由三部分 組成,分別是:
titleView
這又是一個讓我們疑惑的屬性,按照常理我們認(rèn)為這是一個UILabel,通過設(shè)置navigationItem. titleView就是可以搞定title的一切屬性,但是事實(shí)是我們想錯了,這個屬性默認(rèn)為空。所以 當(dāng)我們要單獨(dú)考慮一個viewController的title的時候,就需要為其設(shè)置一個UILabel作為 titleView了,如下所示:
- (void)setTitle:(NSString *)title titleColor:(UIColor *)color{
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 44)];
titleLabel.text = title;
titleLabel.font = [UIFont boldSystemFontOfSize:20.f];
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.textColor = color;
self.navigationItem.titleView = titleLabel;
}
leftNavigationItem
同上,其默認(rèn)也為空(雖然系統(tǒng)會默認(rèn)生成“返回”,但這個屬性值依然為空)。所以我們一般 會設(shè)置可控的leftNavigationItem,如下:
- (void)setNavBarCustomBackButton:(NSString *)title target:(id)target action:(SEL)action {
UIImage *image = [UIImage imageSVGNamed:@"icon_arrow_back_white" size:CGSizeMake(20, 20) cache:YES];
UIButton *buttonItem = [UIButton buttonWithType:UIButtonTypeSystem];
buttonItem.tag = 1002;
buttonItem.titleLabel.font = [UIFont systemFontOfSize:16.0];
buttonItem.imageEdgeInsets = UIEdgeInsetsMake(0, -16, 0, 0);
buttonItem.titleEdgeInsets = UIEdgeInsetsMake(0, -19, 0, 0);
[buttonItem setImage:image forState:UIControlStateNormal];
[buttonItem setTitle:title forState:UIControlStateNormal];
[buttonItem setTitleColor:RGB(45, 45, 45) forState:UIControlStateNormal];
[buttonItem addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[buttonItem sizeToFit];
buttonItem.frame = CGRectMake(0, 0, buttonItem.frame.size.width, 40);
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:buttonItem];
}
rightNavigationItem
其設(shè)置與leftNavigationItem一樣,這里不啰嗦了。
對于navigationItem的三個屬性來說,其貫穿與整個App的開發(fā)過程中,我們?yōu)榱舜a的整潔性, 一般不會在viewController中直接設(shè)置,而已通過category來使用。
導(dǎo)航欄切換
iOS10中navigationController的push和pop,前后兩個viewController如果導(dǎo)航欄顏色(或者透明度)不一致, 就會出現(xiàn)導(dǎo)航欄的切換動畫,處理這種情況時應(yīng)該特別小心,如果處理不當(dāng)就會造成bug或者 App體驗(yàn)上的損失。
系統(tǒng)默認(rèn)在導(dǎo)航欄切換的時候會有動畫和毛玻璃效果,其實(shí)這個動畫就是前后Controller的 導(dǎo)航欄顏色的漸變動畫,而且title也會隨著動畫產(chǎn)生“隱去”和“隱現(xiàn)”的效果,這種效果在 一般情況下沒有問題,而且看起來還很不錯,但是下列兩種情況下你應(yīng)該單獨(dú)考慮。
一側(cè)透明一側(cè)不透明
這種情況下就會產(chǎn)生奇怪的效果,一側(cè)根本看不見導(dǎo)航欄,而系統(tǒng)給我們的是一個漸變的動畫, 然而這個動畫仿佛在iOS10上有bug,會發(fā)生跳轉(zhuǎn)閃爍的情況(在viewWillAppear中設(shè)置導(dǎo)航欄 背景Image的效果會推遲顯示)。如果我們可以接受這個動畫,但是絕不能接受這個閃爍發(fā)生, 可以嘗試著修復(fù)這個bug;但是一些情況下我們并不期望這個動畫產(chǎn)生,做法很簡單把透明一側(cè) 的導(dǎo)航欄直接隱藏掉,這樣前后viewController的導(dǎo)航欄漸進(jìn)動畫就會消失,很多blog上面 說的就是這種方法。
如果在透明一側(cè)的導(dǎo)航欄的透明度是隨著scrollView的contentOffset來變化的,那么隱藏導(dǎo)航欄這種 解決辦法就會失效,因?yàn)槲覀儾荒荇斆У厝ル[藏掉一個透明度不為0的導(dǎo)航欄。這種情況下是 逼著我們?nèi)?shí)現(xiàn)自定義的轉(zhuǎn)場動畫,或者修復(fù)ios10上的bug,這兩種解決辦法都不會太簡單, 我會接下來講解。
兩側(cè)主題相差巨大
如果兩側(cè)導(dǎo)航欄的主題風(fēng)格相差巨大,也就是說前后兩個viewController根本不像是同一個 navigationController中的子congtoller,那么系統(tǒng)中的漸變動畫就會顯得很不協(xié)調(diào),想想 在漸變動畫中出現(xiàn)一個其它不相干的顏色是多么的抓狂。這里的解決辦法也有兩種,一種原理很簡單 ,就是在一側(cè)放棄使用系統(tǒng)的導(dǎo)航欄,而偽造一個假的導(dǎo)航欄,這樣就不會產(chǎn)生漸變動畫。另一種 方法還是自己實(shí)現(xiàn)轉(zhuǎn)場動畫,又回到了這個問題。
市面上一些大的App,在處理上面兩種情況的時候都放棄了系統(tǒng)的漸變動畫,而是采用一種涇渭分明的 轉(zhuǎn)場方式,比如說今日頭條、支付寶等,具體是使用偽導(dǎo)航欄還是自己實(shí)現(xiàn)的轉(zhuǎn)場動畫就不得而知了。 特別說明:以上情況可能不適用于ios10之前,小心調(diào)試吧。
Appearance主題
上面已經(jīng)討論過,為navigationBar上面的item設(shè)置屬性往往會造成困擾,我們有時候無法直接 改變這些item的屬性,所以需要設(shè)置自定義item。Appearance主題是為了設(shè)置整個App的默認(rèn)屬性, 特別是對于UINavigation有用,能夠設(shè)置App導(dǎo)航欄的色彩基調(diào)。如下:
[[UINavigationBar appearance] setTintColor:RGB(0x51, 0x4e, 0x4e)];
[[UINavigationBar appearance] setBarTintColor:RGB(0, 191, 143)];
[[UINavigationBar appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:15], NSFontAttributeName, [UIColor whiteColor], NSForegroundColorAttributeName, nil]];
[[UIBarButtonItem appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
[UIFont systemFontOfSize:15.0], NSFontAttributeName,
RGB(0x51, 0x4e, 0x4e), NSForegroundColorAttributeName,
nil]
forState:UIControlStateNormal];
[[UINavigationBar appearance] setBackIndicatorImage:[UIImage imageSVGNamed:@"icon_arrow_back_white" size:CGSizeMake(20, 20) cache:YES]];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:[UIImage imageSVGNamed:@"icon_arrow_back_white" size:CGSizeMake(20, 20) cache:YES]];
上述代碼最好在App啟動的時候就設(shè)置好,作為導(dǎo)航欄的默認(rèn)設(shè)置,而且在項(xiàng)目的進(jìn)行過程中不要輕易地再去更改。 如果確實(shí)有需求更改navigationBar上的屬性,記得在viewWillAppear或者viewWillDisappear中修改 回去,或者直接使用自定義的item。
總結(jié)
這里我不打算去講解IOS的轉(zhuǎn)場動畫了,因?yàn)檫@個題目還是很大的,我怕說不清楚;再者除非對細(xì)節(jié)有著很高的要求,一般APP也不會去做 導(dǎo)航欄的轉(zhuǎn)場動畫。針對,IOS10中出現(xiàn)的導(dǎo)航欄閃爍問題,我們組的大牛的解決辦法是,放棄NavigationBar上面的一層backgroundView, 統(tǒng)一設(shè)置成clearColor,然后在navigationBar上自己加一層layer,來模擬漸變動畫,這樣就通過擴(kuò)展NavigationBar,避開了系統(tǒng)導(dǎo)航欄 的一些默認(rèn)處理效果。