原文出處: 霖溦(@羽霖溦 )php
前言html
本文整理了一些關於navigationBar的很是規的可是較爲實用的操做,包括利用毛玻璃、動態透明、動態item顏色、動態隱藏,以及頭視圖的動態縮放,並同時涉及了statusBar的動態設置(換色)。ios
先預覽下總體效果:git
Demo詳見GitHub:Demo_AboutNavigationBar
爲了便於展現,類沒有複用,也沒有繼承共有的父類,全部存在大量「有絲分裂」的重複代碼。。。說白了就是懶。。。千萬別學我就是了。github
這裏所謂的不要浪費,只是我的的偏好,固然也是順遂了蘋果的UI特點之一:毛玻璃穿透效果。web
通常界面上是有兩塊毛玻璃的:navigationBar和tabBar,不少APP都會自定義這兩塊Bar,不只是顏色,甚至是控件自己。單考慮利用系統的Bar的話,若是不自定義顏色,就是利用毛玻璃效果自己了,然而不少APP,也並未利用起這個毛玻璃效果,簡書的就是這樣,不過簡書應該是自定義了navigationBar?或許。編程
(這裏插一句,就是QQ的某次更新,更新以後,navigationBar的藍色變成了毛玻璃,以後的一次更新又換了回來……多是被吐槽太多?這裏應該是用戶習慣先入爲主的問題了;還有就是微信,微信應該算得上是個十分純粹的iOS風格的APP,有留意過的話,也會發現其navigationBar是黑色的毛玻璃,或許是barStyle = UIBarStyleBlack
,由於微信也是有不少非原生的處理的。)瀏覽器
下面結合tableView介紹下其與navigationBar的毛玻璃的做用效果。知道我可能要囉嗦什麼的朋友可自行跳過這部份內容。微信
先對比下這兩種效果(圖不太清晰,仔細看navigationBar,仍是很容易對比出來差異的):架構
有毛玻璃穿透效果的效果
沒有毛玻璃穿透效果
1 2 3 4 5 6 7 8 9 10 11 |
- (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的遮擋,然而這樣的處理會產生一個bug,就是tableView總體竟然下移了64……,而後就有了這段解決代碼:
1 2 3 |
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { self.automaticallyAdjustsScrollViewInsets = NO; } |
這是iOS7的特性之一,navigationBar自適應scrollView滑動視圖,然而由於咱們的+64的處理,致使系統的這個默認開啓的功能就成了自做多情。若是
1 |
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)]; |
這樣作,以前的「bug」天然就會沒有,並且咱們想要的毛玻璃效果就會天然的展示出來(如上圖一),可是由於系統的自適應,在某些特殊狀況下,依舊會產生不可控的bug,因此剛纔處理bug的代碼,仍是有必要的。
配合:
1 2 3 |
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) { self.automaticallyAdjustsScrollViewInsets = NO; } |
這樣初始化tableView:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- (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
內嵌邊距,tableView的frame依舊是全屏尺寸的,只是設置了下contentInset
使得其內容視圖的尺寸改變了,scrollIndicatorInsets
是右側的滑動指示器,要跟着一塊兒才天然。若是當前頁還有tabBar的話,只須要設置UIEdgeInsetsMake
的bottom
值就行。這樣,上圖一的毛玻璃穿透效果就天然的釋放了出來。
contentInset
帶來的小問題1.webView初始加載時底部的黑條
若是使用上述方式初始化webView(一樣是滑動視圖,有scrollView屬性,可設置scrollView.contentInset
),至於所謂的「黑條」,大體相似於下面的效果:
黑條出如今視圖底部,通常高度爲64,也就是contentInset
的top
值,上圖的由於設置關係要大不少。相信不少人都在瀏覽APP的webView時,在web加載完以前的空白頁時,見到過這一現象。
關於這以現象的解決方法網上也有不少,我通常是在初始化時,將contentOffset.y
設置一個(-64)的偏移量,這樣,初始進入webView時,在不滑動頁面的狀況下,是不可見的,這算是較爲溫和的解決方法吧,還有一種就是,初始化時設置opaque=NO
,也就是使得view透明,可是在加載以後,要設置opaque=YES
,也就是默認不透明,這樣,黑條是完全不可見的。
2.滑動偏移量改變
在實現一些滑動視圖的動態變化的處理中,例如稍後提到的動態縮放,通常會用代理檢測contentOffset.y
值,可是由於設置了contentInset.top
,此時contentOffset.y
值也就發生了變化:
1 2 3 4 |
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat offsetY = scrollView.contentOffset.y + self.tableView.contentInset.top;//注意 } |
初始再也不是0,而是-64,也就是-top
。
費了一些功夫,都只是爲了實現毛玻璃效果,至於爲何要這個毛玻璃穿透效果,我也說很差,喜愛也罷,設計風格也罷,至少,感受這樣作出的APP,不用專門考慮各類Bar的配色什麼的,動態模糊穿透的彩色,也的確很漂亮,至少,iOS的UI風格有這樣的趨勢。也不要小瞧這個毛玻璃效果,真正實現一個高效的毛玻璃,也就是動態模糊渲染,也是很麻煩的,一些三方庫,爲了版本適配(適配iOS7),自定義的毛玻璃,也是利用了系統的toolBar。看過一些文章,介紹來介紹去,仍是說,用系統在iOS8以後提供的API實現毛玻璃是最好的方式。
下圖(渣圖……)是我最近封裝了有一陣子的一個自定義控件,一樣是爲了效果,用toolBar自定義了一塊毛玻璃,這個控件還未完全完善,稍後會開源,下圖只是其中一種樣式,這個控件的自定義程度仍是很是高的。
有時候,咱們須要將navigationBar設置透明,但不是隱藏,由於還須要其item控件(返回鍵什麼的),雖然navigationBar是繼承於UIView的,可是直接設置其alpha
是無效的,應該是由於navigationBar複合的視圖層級:
根據視圖層級關係,咱們用這個十分簡單的方法來設置navigationBar的透明:
1 |
[[[self.navigationController.navigationBar subviews] objectAtIndex:0] setAlpha:0]; |
關於navigationBar設置透明的方法,不知上述一種,還有許多,詳見此文,很是具備參考價值:
若是想像QQ空間或者微博那樣動態的改變透明度,只須要在scrollViewDidScroll
方法中,動態去設置alpha值就行,什麼時候開始改變、變化率全憑自定義的參數控制,具體詳見Demo中的代碼。
改變item的顏色很簡單,只需設置:
1 |
self.navigationController.navigationBar.tintColor = [UIColor blackColor]; |
動態設置的方式同剛纔的alpha。這裏主要說的是statusBar的顏色或者說樣式的改變。
iOS7以後,statusBar再也不是全局屬性,每一個VC均可自行控制statusBar的樣式,雖然樣式只有簡單的字體黑或白兩種,可是在不少狀況下都是頗有用的,尤爲是上面的navigationBar的alpha動態改變,在QQ空間中就有這個效果。
在設置statusBar樣式以前,須要作一個處理,並且是針對navigationBar的處理,在使用了navigationController以後,直接設置某一個VC的statusBar的樣式是無效的,由於navigationBar是惟一的,全部壓棧推出的VC,都是navigationController的子控制器,這就須要指定statusBar樣式改變的VC爲當前的topVC,具體方式網上也有不少,這裏只介紹我的使用的一種。
首先建立一個繼承於UINavigationController的子類,在這個類中實現下面的方法:
1 2 3 4 |
- (UIViewController *)childViewControllerForStatusBarStyle { return self.topViewController; } |
或者是一樣效果的這個方法:
1 2 3 4 5 |
- (UIStatusBarStyle)preferredStatusBarStyle { UIViewController * topVC = self.topViewController; return [topVC preferredStatusBarStyle]; } |
以後,只需在要改變statusBar樣式的VC類中實現:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifdef __IPHONE_7_0 - (UIStatusBarStyle)preferredStatusBarStyle { if (_statusBarStyleControl) { return UIStatusBarStyleDefault; } else { return UIStatusBarStyleLightContent; } } - (BOOL)prefersStatusBarHidden { return NO; } #endif |
__IPHONE_7_0
是系統的宏,這裏用來版本適配,這個不寫貌似沒有關係?由於以前試了iOS7如下的系統沒有崩潰,iOS7以前沒有這個方法,應該是不會執行的,也就不會崩潰。
preferredStatusBarStyle
就是控制用來控制statusBar顏色或者說樣式的,_statusBarStyleControl
是自定義的一個用來動態控制的BOOL屬性。
prefersStatusBarHidden
這個控制statusBar的顯示隱藏,建議NO或直接默認不寫,若是設置隱藏,視圖會總體上移20,效果不太好,看具體需求。
至於控制statusBar的改變,也是在scrollViewDidScroll
代理中動態實現,例如某一狀況下觸發以下:
1 2 3 4 |
_statusBarStyleControl = YES; if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) { [self setNeedsStatusBarAppearanceUpdate]; } |
第一步是設置以前提到的_statusBarStyleControl
標誌位的值的切換,第二部是最重要的setNeedsStatusBarAppearanceUpdate
,這個系統的方法是在改變statusBar顯示樣式以前必須執行的,不然preferredStatusBarStyle
不會再當前視圖加載完成後再次執行。
navigationBar的隱藏很簡單:
1 |
[self.navigationController setNavigationBarHidden:YES animated:YES]; |
這個方法可使動態隱藏時有動畫效果,不會顯得突兀。
動態隱藏的效果有兩個場景:一個就是例如簡書這樣的,在瀏覽時,上滑,navigationBar隱藏,下滑navigationBar顯示,在這期間,手指是不鬆開的,這須要實時檢測當前是上滑仍是下滑;第二個場景是Safari瀏覽器那樣的,滑動後鬆手,根據上滑仍是下滑設置隱藏(Safari的navigationBar不是隱藏,只是變化)。這樣的兩種場景雖然很類似,但就是鬆不鬆手的問題,處理方式和體驗也是徹底不一樣的。
1.第二場景,鬆手
這個處理十分簡單:
1 2 3 4 5 6 7 8 9 |
- (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這個量,在上滑和下滑時,變化極小(小數),可是由於方向不一樣,有正負之分,這就很好處理了。
2.第一場景,不鬆手
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
- (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) { //下滑趨勢,顯示 [self.navigationController setNavigationBarHidden:NO animated:YES]; } else { //上滑趨勢,隱藏 [self.navigationController setNavigationBarHidden:YES animated:YES]; } } else { [self.navigationController setNavigationBarHidden:NO animated:YES]; } } |
這裏的offsetY > 64
只是爲了在視圖滑過navigationBar的高度以後纔開始處理,防止影響展現效果。
panTranslationY
是scrollView的pan手勢的手指位置的y值,這個方法是我的本身想到的,可能不是太好,由於panTranslationY
這個值在較小幅度上下滑動時,可能都爲正或都爲負,這就使得這一方式不太靈敏,若是有更好的方法,歡迎留言交流。
這個效果純粹是爲了仿簡書的我的信息的頭像的縮放效果。
頭像視圖的佈局直接利用了navigationBar的titleView,代碼仍是很好理解的,視圖層級關係以下:
頭像的一半是「懸空」的。
縮放只是滑動代理監聽時的一個動態縮放的過程,縮放率同以前說的alpha的動態變化同樣,自行設置常量,這裏注意縮放過程的分段執行,爲了效果,這個有下拉放大回彈的效果,這部分不是簡書的效果:
1 |
_topImageView.transform = CGAffineTransformMakeScale(1 - offsetY/300, 1 - offsetY/300); |
若是隻是設置縮放,效果以下:
爲了使縮放過程當中位置相對保持,以前想到了改變錨點的方式,然而效果不好,最終仍是以簡單的方式實現:
1 2 3 |
CGRect frame = _topImageView.frame; frame.origin.y = 5; _topImageView.frame = frame; |
就是保持y的座標爲初始值。
下面的這個仿微博的效果,也是基於一樣的原理,只是要注意圖層關係,詳細參考代碼,再也不贅述。
這算是個比較大的話題了,前面提到的那篇文章中有關於此的詳細論述,這裏只是簡單說起,稍後有機會,會專門寫一篇關於側滑返回的自定義實現。
iOS7以後,navigationController推出的視圖,只要返回按鈕不自定義覆蓋,或者相關屬性默認,右滑屏幕左邊緣,能夠直接pop返回,這是個十分方便的功能。關於實現和自定義實現,網上也有太多的文章。這裏只是說明一點,就是在設置navigationBar透明以後,使用側滑返回的過程當中,navigationBar會忽然出現,顯得十分突兀,這也是目前微博APP的效果,QQ和微信都針對此作了不一樣的處理,本文的Demo中也嘗試作了必定的處理,可是效果並非十分理想的,就是中斷側滑返回動做時,navigationBar閃的問題。這個問題已經超出本文內容的範圍,有機會會在從此的文章中介紹。
參考文章:
1.iOS開發的一些奇巧淫技
2.自定義iOS7導航欄背景,標題和返回按鈕文字顏色
3.Removing the title text of an iOS 7 UIBarButtonItem
4.iOS 實現ScrollView 上滑隱藏Navigationbar,下滑顯示
5.iOS 7 改變 app 的外觀(NavigationBar,TabBar,StatusBar)
6.iOS不一樣版本適配問題(#ifdef __IPHONE_7_0)
7. __IPHONE_OS_VERSION_MIN_REQUIRED
8.IOS開發之不一樣版本適配問題3(#ifdef __IPHONE_7_0 BaseSDK Development Target)
9.IOS開發之不一樣版本適配問題2(#ifdef __IPHONE_7_0)
10.ios7 statusBar的字體顏色怎麼設置爲白色的呢
11.IOS上 關於狀態欄的相關設置(UIStatusBar)
12.IOS7怎麼修改Navigation Bar上的返回按鈕文本顏色,箭頭顏色以及導航欄按鈕的顏色
13.怎麼把頂部這個navigationbar設置爲透明呢,可以讓下面的圖片顯示出來,可是返回按鈕不透明?
14.iOS navigationbar全透明的方法
15.IOS中設置UINavigationBar的各類樣式(圖片/透明效果/下方內容顯示狀況)
16.iOS開發UI篇—CAlayer層的屬性
17.iOS – UINavigationController簡介
問啊-定製化IT教育平臺,牛人一對一服務,有問必答,開發編程社交頭條 官方網站:www.wenaaa.com 下載問啊APP,參與官方懸賞,賺百元現金。
QQ羣290551701 彙集不少互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志於從事IT行業人員進入!