前言
本文整理了一些關(guān)于navigationBar的非常規(guī)的但是較為實(shí)用的操作搞旭,包括利用毛玻璃、動(dòng)態(tài)透明菇绵、動(dòng)態(tài)item顏色肄渗、動(dòng)態(tài)隱藏,以及頭視圖的動(dòng)態(tài)縮放咬最,并同時(shí)涉及了statusBar的動(dòng)態(tài)設(shè)置(換色)翎嫡。
先預(yù)覽下整體效果:
Demo詳見GitHub:Demo_AboutNavigationBar
為了便于展示,類沒有復(fù)用永乌,也沒有繼承共有的父類惑申,所有存在大量“有絲分裂”的重復(fù)代碼。翅雏。圈驼。說白了就是懶。望几。绩脆。千萬別學(xué)我就是了。
1.不要“浪費(fèi)”了這塊毛玻璃
這里所謂的不要浪費(fèi),只是個(gè)人的偏好靴迫,當(dāng)然也是順?biāo)炝颂O果的UI特色之一:毛玻璃穿透效果惕味。
一般界面上是有兩塊毛玻璃的:navigationBar和tabBar,很多APP都會(huì)自定義這兩塊Bar玉锌,不僅是顏色名挥,甚至是控件本身。單考慮利用系統(tǒng)的Bar的話主守,如果不自定義顏色禀倔,就是利用毛玻璃效果本身了,然而很多APP参淫,也并未利用起這個(gè)毛玻璃效果蹋艺,簡(jiǎn)書的就是這樣,不過簡(jiǎn)書應(yīng)該是自定義了navigationBar黄刚?或許。
(這里插一句民效,就是QQ的某次更新憔维,更新之后,navigationBar的藍(lán)色變成了毛玻璃畏邢,之后的一次更新又換了回來……可能是被吐槽太多业扒?這里應(yīng)該是用戶習(xí)慣先入為主的問題了;還有就是微信舒萎,微信應(yīng)該算得上是個(gè)十分純粹的iOS風(fēng)格的APP程储,有留意過的話,也會(huì)發(fā)現(xiàn)其navigationBar是黑色的毛玻璃臂寝,或許是barStyle = UIBarStyleBlack
章鲤,因?yàn)槲⑿乓彩怯泻芏喾窃奶幚淼摹#?br>
下面結(jié)合tableView介紹下其與navigationBar的毛玻璃的作用效果咆贬。知道我可能要啰嗦什么的朋友可自行跳過這部分內(nèi)容败徊。
先對(duì)比下這兩種效果(圖不太清晰,仔細(xì)看navigationBar掏缎,還是很容易對(duì)比出來差別的):
1.1.一般的處理
- (UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, ScreenWidth, ScreenHeight - 64)];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor whiteColor];
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
}
return _tableView;
}
相信有很多人大概是以與這樣類似的方式去初始化tableView的皱蹦,origin.y=64,總高度-64眷蜈,都是為了防止navigationBar的遮擋沪哺,然而這樣的處理會(huì)產(chǎn)生一個(gè)bug,就是tableView整體居然下移了64……酌儒,然后就有了這段解決代碼:
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}
這是iOS7的特性之一辜妓,navigationBar自適應(yīng)scrollView滑動(dòng)視圖,然而因?yàn)槲覀兊?64的處理,導(dǎo)致系統(tǒng)的這個(gè)默認(rèn)開啟的功能就成了自作多情嫌拣。如果
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)];
這樣做柔袁,之前的“bug”自然就會(huì)沒有,而且我們想要的毛玻璃效果就會(huì)自然的展現(xiàn)出來(如上圖一)异逐,但是因?yàn)橄到y(tǒng)的自適應(yīng)捶索,在某些特殊情況下,依舊會(huì)產(chǎn)生不可控的bug灰瞻,所以剛才處理bug的代碼腥例,還是有必要的。
1.2.“釋放”系統(tǒng)的毛玻璃效果的處理
配合:
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}
這樣初始化tableView:
- (UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor whiteColor];
_tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
_tableView.scrollIndicatorInsets = UIEdgeInsetsMake(64, 0, 0, 0);
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
}
return _tableView;
}
這里利用了edgeInset
內(nèi)嵌邊距酝润,tableView的frame依舊是全屏尺寸的燎竖,只是設(shè)置了下contentInset
使得其內(nèi)容視圖的尺寸改變了,scrollIndicatorInsets
是右側(cè)的滑動(dòng)指示器要销,要跟著一起才自然构回。如果當(dāng)前頁還有tabBar的話,只需要設(shè)置UIEdgeInsetsMake
的bottom
值就行疏咐。這樣纤掸,上圖一的毛玻璃穿透效果就自然的釋放了出來。
1.3.設(shè)置contentInset
帶來的小問題
-
1.webView初始加載時(shí)底部的黑條
如果使用上述方式初始化webView(同樣是滑動(dòng)視圖浑塞,有scrollView屬性借跪,可設(shè)置scrollView.contentInset
),至于所謂的“黑條”酌壕,大致類似于下面的效果:
黑條出現(xiàn)在視圖底部掏愁,一般高度為64,也就是contentInset
的top
值卵牍,上圖的因?yàn)樵O(shè)置關(guān)系要大很多果港。相信很多人都在瀏覽APP的webView時(shí),在web加載完之前的空白頁時(shí)辽慕,見到過這一現(xiàn)象京腥。
關(guān)于這以現(xiàn)象的解決方法網(wǎng)上也有很多,我一般是在初始化時(shí)溅蛉,將contentOffset.y
設(shè)置一個(gè)(-64)的偏移量公浪,這樣,初始進(jìn)入webView時(shí)船侧,在不滑動(dòng)頁面的情況下欠气,是不可見的,這算是較為溫和的解決方法吧镜撩,還有一種就是预柒,初始化時(shí)設(shè)置opaque=NO
队塘,也就是使得view透明,但是在加載之后宜鸯,要設(shè)置opaque=YES
憔古,也就是默認(rèn)不透明,這樣淋袖,黑條是徹底不可見的鸿市。
-
2.滑動(dòng)偏移量改變
在實(shí)現(xiàn)一些滑動(dòng)視圖的動(dòng)態(tài)變化的處理中,例如稍后提到的動(dòng)態(tài)縮放即碗,一般會(huì)用代理檢測(cè)contentOffset.y
值焰情,但是因?yàn)樵O(shè)置了contentInset.top
,此時(shí)contentOffset.y
值也就發(fā)生了變化:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y + self.tableView.contentInset.top;//注意
}
初始不再是0剥懒,而是-64内舟,也就是-top
。
1.4.為什么需要毛玻璃效果
費(fèi)了一些功夫初橘,都只是為了實(shí)現(xiàn)毛玻璃效果验游,至于為什么要這個(gè)毛玻璃穿透效果,我也說不好保檐,喜好也罷批狱,設(shè)計(jì)風(fēng)格也罷,至少展东,感覺這樣做出的APP,不用專門考慮各種Bar的配色什么的炒俱,動(dòng)態(tài)模糊穿透的彩色盐肃,也的確很漂亮,至少权悟,iOS的UI風(fēng)格有這樣的趨勢(shì)砸王。也不要小瞧這個(gè)毛玻璃效果,真正實(shí)現(xiàn)一個(gè)高效的毛玻璃峦阁,也就是動(dòng)態(tài)模糊渲染谦铃,也是很麻煩的,一些三方庫榔昔,為了版本適配(適配iOS7)驹闰,自定義的毛玻璃,也是利用了系統(tǒng)的toolBar撒会∴诶剩看過一些文章,介紹來介紹去诵肛,還是說屹培,用系統(tǒng)在iOS8之后提供的API實(shí)現(xiàn)毛玻璃是最好的方式。
下圖(渣圖……)是我最近封裝了有一陣子的一個(gè)自定義控件,同樣是為了效果褪秀,用toolBar自定義了一塊毛玻璃蓄诽,這個(gè)控件還未徹底完善,稍后會(huì)開源媒吗,下圖只是其中一種樣式仑氛,這個(gè)控件的自定義程度還是非常高的。
2.navigationBar的透明
有時(shí)候蝴猪,我們需要將navigationBar設(shè)置透明调衰,但不是隱藏,因?yàn)檫€需要其item控件(返回鍵什么的)自阱,雖然navigationBar是繼承于UIView的嚎莉,但是直接設(shè)置其alpha
是無效的,應(yīng)該是因?yàn)閚avigationBar復(fù)合的視圖層級(jí):
根據(jù)視圖層級(jí)關(guān)系沛豌,我們用這個(gè)十分簡(jiǎn)單的方法來設(shè)置navigationBar的透明:
[[[self.navigationController.navigationBar subviews] objectAtIndex:0] setAlpha:0];
關(guān)于navigationBar設(shè)置透明的方法趋箩,不知上述一種,還有許多加派,詳見此文叫确,非常具有參考價(jià)值:
怎么把頂部這個(gè)navigationbar設(shè)置為透明呢,能夠讓下面的圖片顯示出來芍锦,但是返回按鈕不透明竹勉?
如果想像QQ空間或者微博那樣動(dòng)態(tài)的改變透明度,只需要在scrollViewDidScroll
方法中娄琉,動(dòng)態(tài)去設(shè)置alpha值就行次乓,何時(shí)開始改變、變化率全憑自定義的參數(shù)控制孽水,具體詳見Demo中的代碼票腰。
3.動(dòng)態(tài)statusBar顏色
改變item的顏色很簡(jiǎn)單,只需設(shè)置:
self.navigationController.navigationBar.tintColor = [UIColor blackColor];
動(dòng)態(tài)設(shè)置的方式同剛才的alpha女气。這里主要說的是statusBar的顏色或者說樣式的改變杏慰。
iOS7之后,statusBar不再是全局屬性炼鞠,每個(gè)VC都可自行控制statusBar的樣式缘滥,雖然樣式只有簡(jiǎn)單的字體黑或白兩種,但是在很多情況下都是很有用的谒主,尤其是上面的navigationBar的alpha動(dòng)態(tài)改變完域,在QQ空間中就有這個(gè)效果。
在設(shè)置statusBar樣式之前瘩将,需要做一個(gè)處理吟税,而且是針對(duì)navigationBar的處理凹耙,在使用了navigationController之后,直接設(shè)置某一個(gè)VC的statusBar的樣式是無效的肠仪,因?yàn)閚avigationBar是唯一的肖抱,所有壓棧推出的VC,都是navigationController的子控制器异旧,這就需要指定statusBar樣式改變的VC為當(dāng)前的topVC意述,具體方式網(wǎng)上也有很多,這里只介紹個(gè)人使用的一種吮蛹。
首先創(chuàng)建一個(gè)繼承于UINavigationController的子類荤崇,在這個(gè)類中實(shí)現(xiàn)下面的方法:
- (UIViewController *)childViewControllerForStatusBarStyle
{
return self.topViewController;
}
或者是同樣效果的這個(gè)方法:
- (UIStatusBarStyle)preferredStatusBarStyle
{
UIViewController * topVC = self.topViewController;
return [topVC preferredStatusBarStyle];
}
之后,只需在要改變statusBar樣式的VC類中實(shí)現(xiàn):
#ifdef __IPHONE_7_0
- (UIStatusBarStyle)preferredStatusBarStyle
{
if (_statusBarStyleControl) {
return UIStatusBarStyleDefault;
}
else {
return UIStatusBarStyleLightContent;
}
}
- (BOOL)prefersStatusBarHidden
{
return NO;
}
#endif
__IPHONE_7_0
是系統(tǒng)的宏潮针,這里用來版本適配术荤,這個(gè)不寫貌似沒有關(guān)系?因?yàn)橹霸嚵薸OS7以下的系統(tǒng)沒有崩潰每篷,iOS7之前沒有這個(gè)方法瓣戚,應(yīng)該是不會(huì)執(zhí)行的,也就不會(huì)崩潰焦读。
preferredStatusBarStyle
就是控制用來控制statusBar顏色或者說樣式的子库,_statusBarStyleControl
是自定義的一個(gè)用來動(dòng)態(tài)控制的BOOL屬性。
prefersStatusBarHidden
這個(gè)控制statusBar的顯示隱藏矗晃,建議NO或直接默認(rèn)不寫仑嗅,如果設(shè)置隱藏,視圖會(huì)整體上移20张症,效果不太好无畔,看具體需求。
至于控制statusBar的改變吠冤,也是在scrollViewDidScroll
代理中動(dòng)態(tài)實(shí)現(xiàn),例如某一情況下觸發(fā)如下:
_statusBarStyleControl = YES;
if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
[self setNeedsStatusBarAppearanceUpdate];
}
第一步是設(shè)置之前提到的_statusBarStyleControl
標(biāo)志位的值的切換恭理,第二部是最重要的setNeedsStatusBarAppearanceUpdate
拯辙,這個(gè)系統(tǒng)的方法是在改變statusBar顯示樣式之前必須執(zhí)行的,否則preferredStatusBarStyle
不會(huì)再當(dāng)前視圖加載完成后再次執(zhí)行颜价。
4.navigationBar的動(dòng)態(tài)隱藏
navigationBar的隱藏很簡(jiǎn)單:
[self.navigationController setNavigationBarHidden:YES animated:YES];
這個(gè)方法可以使動(dòng)態(tài)隱藏時(shí)有動(dòng)畫效果涯保,不會(huì)顯得突兀。
動(dòng)態(tài)隱藏的效果有兩個(gè)場(chǎng)景:一個(gè)就是例如簡(jiǎn)書這樣的周伦,在瀏覽時(shí)夕春,上滑,navigationBar隱藏专挪,下滑navigationBar顯示及志,在這期間片排,手指是不松開的,這需要實(shí)時(shí)檢測(cè)當(dāng)前是上滑還是下滑速侈;第二個(gè)場(chǎng)景是Safari瀏覽器那樣的率寡,滑動(dòng)后松手,根據(jù)上滑還是下滑設(shè)置隱藏(Safari的navigationBar不是隱藏倚搬,只是變化)冶共。這樣的兩種場(chǎng)景雖然很相似,但就是松不松手的問題每界,處理方式和體驗(yàn)也是完全不同的捅僵。
- 1.第二場(chǎng)景,松手
這個(gè)處理十分簡(jiǎn)單:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
NSLog(@"======== %lf", velocity.y);
if(velocity.y > 0) {
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
else {
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
}
velocity.y這個(gè)量眨层,在上滑和下滑時(shí)庙楚,變化極小(小數(shù))谐岁,但是因?yàn)榉较虿煌椎欤姓?fù)之分,這就很好處理了伊佃。
- 2.第一場(chǎng)景窜司,不松手
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat offsetY = scrollView.contentOffset.y + _tableView.contentInset.top;//注意
CGFloat panTranslationY = [scrollView.panGestureRecognizer translationInView:self.tableView].y;
if (offsetY > 64) {
if (panTranslationY > 0) { //下滑趨勢(shì),顯示
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
else { //上滑趨勢(shì)航揉,隱藏
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
}
else {
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
}
這里的offsetY > 64
只是為了在視圖滑過navigationBar的高度之后才開始處理塞祈,防止影響展示效果。
panTranslationY
是scrollView的pan手勢(shì)的手指位置的y值帅涂,這個(gè)方法是個(gè)人自己想到的议薪,可能不是太好,因?yàn)?code>panTranslationY這個(gè)值在較小幅度上下滑動(dòng)時(shí)媳友,可能都為正或都為負(fù)斯议,這就使得這一方式不太靈敏,如果有更好的方法醇锚,歡迎留言交流哼御。
5.動(dòng)態(tài)縮放
這個(gè)效果純粹是為了仿簡(jiǎn)書的個(gè)人信息的頭像的縮放效果。
頭像視圖的布局直接利用了navigationBar的titleView焊唬,代碼還是很好理解的恋昼,視圖層級(jí)關(guān)系如下:
頭像的一半是“懸空”的。
縮放只是滑動(dòng)代理監(jiān)聽時(shí)的一個(gè)動(dòng)態(tài)縮放的過程赶促,縮放率同之前說的alpha的動(dòng)態(tài)變化一樣液肌,自行設(shè)置常量,這里注意縮放過程的分段執(zhí)行鸥滨,為了效果嗦哆,這個(gè)有下拉放大回彈的效果谤祖,這部分不是簡(jiǎn)書的效果:
_topImageView.transform = CGAffineTransformMakeScale(1 - offsetY/300, 1 - offsetY/300);
如果只是設(shè)置縮放,效果如下:
為了使縮放過程中位置相對(duì)保持吝秕,之前想到了改變錨點(diǎn)的方式泊脐,然而效果很差,最終還是以簡(jiǎn)單的方式實(shí)現(xiàn):
CGRect frame = _topImageView.frame;
frame.origin.y = 5;
_topImageView.frame = frame;
就是保持y的坐標(biāo)為初始值烁峭。
下面的這個(gè)仿微博的效果容客,也是基于同樣的原理,只是要注意圖層關(guān)系约郁,詳細(xì)參考代碼缩挑,不再贅述。
6.側(cè)滑返回
這算是個(gè)比較大的話題了鬓梅,前面提到的那篇文章中有關(guān)于此的詳細(xì)論述供置,這里只是簡(jiǎn)單提及,稍后有機(jī)會(huì)绽快,會(huì)專門寫一篇關(guān)于側(cè)滑返回的自定義實(shí)現(xiàn)芥丧。
iOS7之后,navigationController推出的視圖坊罢,只要返回按鈕不自定義覆蓋续担,或者相關(guān)屬性默認(rèn),右滑屏幕左邊緣活孩,可以直接pop返回物遇,這是個(gè)十分方便的功能。關(guān)于實(shí)現(xiàn)和自定義實(shí)現(xiàn)憾儒,網(wǎng)上也有太多的文章询兴。這里只是說明一點(diǎn),就是在設(shè)置navigationBar透明之后起趾,使用側(cè)滑返回的過程中诗舰,navigationBar會(huì)突然出現(xiàn),顯得十分突兀训裆,這也是目前微博APP的效果眶根,QQ和微信都針對(duì)此做了不同的處理,本文的Demo中也嘗試做了一定的處理缭保,但是效果并不是十分理想的,就是中斷側(cè)滑返回動(dòng)作時(shí)蝙茶,navigationBar閃的問題艺骂。這個(gè)問題已經(jīng)超出本文內(nèi)容的范圍,有機(jī)會(huì)會(huì)在今后的文章中介紹隆夯。
參考文章:
1.iOS開發(fā)的一些奇巧淫技
2.自定義iOS7導(dǎo)航欄背景,標(biāo)題和返回按鈕文字顏色
3.Removing the title text of an iOS 7 UIBarButtonItem
4.iOS 實(shí)現(xiàn)ScrollView 上滑隱藏Navigationbar,下滑顯示
5.iOS 7 改變 app 的外觀(NavigationBar钳恕,TabBar别伏,StatusBar)
6.iOS不同版本適配問題(#ifdef __IPHONE_7_0)
7. __IPHONE_OS_VERSION_MIN_REQUIRED
8.IOS開發(fā)之不同版本適配問題3(#ifdef __IPHONE_7_0 BaseSDK Development Target)
9.IOS開發(fā)之不同版本適配問題2(#ifdef __IPHONE_7_0)
10.ios7 statusBar的字體顏色怎么設(shè)置為白色的呢
11.IOS上 關(guān)于狀態(tài)欄的相關(guān)設(shè)置(UIStatusBar)
12.IOS7怎么修改Navigation Bar上的返回按鈕文本顏色,箭頭顏色以及導(dǎo)航欄按鈕的顏色
13.怎么把頂部這個(gè)navigationbar設(shè)置為透明呢,能夠讓下面的圖片顯示出來忧额,但是返回按鈕不透明厘肮?
14.iOS navigationbar全透明的方法
15.IOS中設(shè)置UINavigationBar的各種樣式(圖片/透明效果/下方內(nèi)容顯示情況)
16.iOS開發(fā)UI篇—CAlayer層的屬性
17.iOS - UINavigationController簡(jiǎn)介