前言
本文整理了一些關于navigationBar的非正常的但是較為實用的操作,包括利用毛玻璃、動态透明、動态item顔色、動态隐藏,以及頭視圖的動态縮放,并同時涉及了statusBar的動态設定(換色)。
先預覽下整體效果:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICcpJHdz9CX05WZpJ3bt8Gd1F2LcJjcn9WTldWYtl2Pml2ZuMjZ0I2M1ADZ1M2MwU2YlJWLwMjN4YDNx8CXzV2Zh1WafRWYvxGc19CXvlmL1h2cuFWaq5ycldWYtlWLkF2bsBXdvw1LcpDc0RHaiojIsJye.gif)
Demo詳見GitHub:Demo_AboutNavigationBar
為了便于展示,類沒有複用,也沒有繼承共有的父類,所有存在大量“有絲分裂”的重複代碼。。。說白了就是懶。。。千萬别學我就是了。
1.不要“浪費”了這塊毛玻璃
這裡所謂的不要浪費,隻是個人的偏好,當然也是順遂了蘋果的UI特色之一:毛玻璃穿透效果。
一般界面上是有兩塊毛玻璃的:navigationBar和tabBar,很多APP都會自定義這兩塊Bar,不僅是顔色,甚至是控件本身。單考慮利用系統的Bar的話,如果不自定義顔色,就是利用毛玻璃效果本身了,然而很多APP,也并未利用起這個毛玻璃效果,簡書的就是這樣,不過簡書應該是自定義了navigationBar?或許。
(這裡插一句,就是QQ的某次更新,更新之後,navigationBar的藍色變成了毛玻璃,之後的一次更新又換了回來……可能是被吐槽太多?這裡應該是使用者習慣先入為主的問題了;還有就是微信,微信應該算得上是個十分純粹的iOS風格的APP,有留意過的話,也會發現其navigationBar是黑色的毛玻璃,或許是
barStyle = UIBarStyleBlack
,因為微信也是有很多非原生的處理的。)
下面結合tableView介紹下其與navigationBar的毛玻璃的作用效果。知道我可能要啰嗦什麼的朋友可自行跳過這部分内容。
先對比下這兩種效果(圖不太清晰,仔細看navigationBar,還是很容易對比出來差别的):
有毛玻璃穿透效果的效果
沒有毛玻璃穿透效果
1.1.一般的處理
- (UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(, , ScreenWidth, ScreenHeight - )];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor whiteColor];
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
}
return _tableView;
}
相信有很多人大概是以與這樣類似的方式去初始化tableView的,origin.y=64,總高度-64,都是為了防止navigationBar的遮擋,然而這樣的處理會産生一個bug,就是tableView整體居然下移了64……,然後就有了這段解決代碼:
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}
這是iOS7的特性之一,navigationBar自适應scrollView滑動視圖,然而因為我們的+64的處理,導緻系統的這個預設開啟的功能就成了自作多情。如果
這樣做,之前的“bug”自然就會沒有,而且我們想要的毛玻璃效果就會自然的展現出來(如上圖一),但是因為系統的自适應,在某些特殊情況下,依舊會産生不可控的bug,是以剛才處理bug的代碼,還是有必要的。
1.2.“釋放”系統的毛玻璃效果的處理
配合:
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
self.automaticallyAdjustsScrollViewInsets = NO;
}
這樣初始化tableView:
- (UITableView *)tableView
{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(, , ScreenWidth, ScreenHeight)];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor whiteColor];
_tableView.contentInset = UIEdgeInsetsMake(, , , );
_tableView.scrollIndicatorInsets = UIEdgeInsetsMake(, , , );
_tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
}
return _tableView;
}
這裡利用了
edgeInset
内嵌邊距,tableView的frame依舊是全屏尺寸的,隻是設定了下
contentInset
使得其内容視圖的尺寸改變了,
scrollIndicatorInsets
是右側的滑動訓示器,要跟着一起才自然。如果目前頁還有tabBar的話,隻需要設定
UIEdgeInsetsMake
的
bottom
值就行。這樣,上圖一的毛玻璃穿透效果就自然的釋放了出來。
1.3.設定 contentInset
帶來的小問題
contentInset
-
1.webView初始加載時底部的黑條
如果使用上述方式初始化webView(同樣是滑動視圖,有scrollView屬性,可設定
),至于所謂的“黑條”,大緻類似于下面的效果:scrollView.contentInset
黑條出現在視圖底部,一般高度為64,也就是iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回
的contentInset
top
值,上圖的因為設定關系要大很多。相信很多人都在浏覽APP的webView時,在web加載完之前的空白頁時,見到過這一現象。
關于這以現象的解決方法網上也有很多,我一般是在初始化時,将
設定一個(-64)的偏移量,這樣,初始進入webView時,在不滑動頁面的情況下,是不可見的,這算是較為溫和的解決方法吧,還有一種就是,初始化時設定contentOffset.y
,也就是使得view透明,但是在加載之後,要設定opaque=NO
,也就是預設不透明,這樣,黑條是徹底不可見的。opaque=YES
-
2.滑動偏移量改變
在實作一些滑動視圖的動态變化的進行中,例如稍後提到的動态縮放,一般會用代理檢測
值,但是因為設定了contentOffset.y
,此時contentInset.top
值也就發生了變化:contentOffset.y
初始不再是0,而是-64,也就是- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat offsetY = scrollView.contentOffset.y + self.tableView.contentInset.top;//注意 }
。-top
1.4.為什麼需要毛玻璃效果
費了一些功夫,都隻是為了實作毛玻璃效果,至于為什麼要這個毛玻璃穿透效果,我也說不好,喜好也罷,設計風格也罷,至少,感覺這樣做出的APP,不用專門考慮各種Bar的配色什麼的,動态模糊穿透的彩色,也的确很漂亮,至少,iOS的UI風格有這樣的趨勢。也不要小瞧這個毛玻璃效果,真正實作一個高效的毛玻璃,也就是動态模糊渲染,也是很麻煩的,一些三方庫,為了版本适配(适配iOS7),自定義的毛玻璃,也是利用了系統的toolBar。看過一些文章,介紹來介紹去,還是說,用系統在iOS8之後提供的API實作毛玻璃是最好的方式。
下圖(渣圖……)是我最近封裝了有一陣子的一個自定義控件,同樣是為了效果,用toolBar自定義了一塊毛玻璃,這個控件還未徹底完善,稍後會開源,下圖隻是其中一種樣式,這個控件的自定義程度還是非常高的。
2.navigationBar的透明
有時候,我們需要将navigationBar設定透明,但不是隐藏,因為還需要其item控件(傳回鍵什麼的),雖然navigationBar是繼承于UIView的,但是直接設定其
alpha
是無效的,應該是因為navigationBar複合的視圖層級:
根據視圖層級關系,我們用這個十分簡單的方法來設定navigationBar的透明:
關于navigationBar設定透明的方法,不知上述一種,還有許多,詳見此文,非常具有參考價值:
怎麼把頂部這個navigationbar設定為透明呢,能夠讓下面的圖檔顯示出來,但是傳回按鈕不透明?
如果想像QQ空間或者微網誌那樣動态的改變透明度,隻需要在
scrollViewDidScroll
方法中,動态去設定alpha值就行,何時開始改變、變化率全憑自定義的參數控制,具體詳見Demo中的代碼。
3.動态statusBar顔色
改變item的顔色很簡單,隻需設定:
動态設定的方式同剛才的alpha。這裡主要說的是statusBar的顔色或者說樣式的改變。
iOS7之後,statusBar不再是全局屬性,每個VC都可自行控制statusBar的樣式,雖然樣式隻有簡單的字型黑或白兩種,但是在很多情況下都是很有用的,尤其是上面的navigationBar的alpha動态改變,在QQ空間中就有這個效果。
在設定statusBar樣式之前,需要做一個處理,而且是針對navigationBar的處理,在使用了navigationController之後,直接設定某一個VC的statusBar的樣式是無效的,因為navigationBar是唯一的,所有壓棧推出的VC,都是navigationController的子控制器,這就需要指定statusBar樣式改變的VC為目前的topVC,具體方式網上也有很多,這裡隻介紹個人使用的一種。
首先建立一個繼承于UINavigationController的子類,在這個類中實作下面的方法:
- (UIViewController *)childViewControllerForStatusBarStyle
{
return self.topViewController;
}
或者是同樣效果的這個方法:
- (UIStatusBarStyle)preferredStatusBarStyle
{
UIViewController * topVC = self.topViewController;
return [topVC preferredStatusBarStyle];
}
之後,隻需在要改變statusBar樣式的VC類中實作:
#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
代理中動态實作,例如某一情況下觸發如下:
_statusBarStyleControl = YES;
if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) {
[self setNeedsStatusBarAppearanceUpdate];
}
第一步是設定之前提到的
_statusBarStyleControl
标志位的值的切換,第二部是最重要的
setNeedsStatusBarAppearanceUpdate
,這個系統的方法是在改變statusBar顯示樣式之前必須執行的,否則
preferredStatusBarStyle
不會再目前視圖加載完成後再次執行。
4.navigationBar的動态隐藏
navigationBar的隐藏很簡單:
這個方法可以使動态隐藏時有動畫效果,不會顯得突兀。
動态隐藏的效果有兩個場景:一個就是例如簡書這樣的,在浏覽時,上滑,navigationBar隐藏,下滑navigationBar顯示,在這期間,手指是不松開的,這需要實時檢測目前是上滑還是下滑;第二個場景是Safari浏覽器那樣的,滑動後松手,根據上滑還是下滑設定隐藏(Safari的navigationBar不是隐藏,隻是變化)。這樣的兩種場景雖然很相似,但就是松不松手的問題,處理方式和體驗也是完全不同的。
-
1.第二場景,松手
這個處理十分簡單:
velocity.y這個量,在上滑和下滑時,變化極小(小數),但是因為方向不同,有正負之分,這就很好處理了。- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{ NSLog(@"======== %lf", velocity.y); if(velocity.y > ) { [self.navigationController setNavigationBarHidden:YES animated:YES]; } else { [self.navigationController setNavigationBarHidden:NO animated:YES]; } }
- 2.第一場景,不松手
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat offsetY = scrollView.contentOffset.y + _tableView.contentInset.top;//注意 CGFloat panTranslationY = [scrollView.panGestureRecognizer translationInView:self.tableView].y; if (offsetY > ) { if (panTranslationY > ) { //下滑趨勢,顯示 [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
這個值在較小幅度上下滑動時,可能都為正或都為負,這就使得這一方式不太靈敏,如果有更好的方法,歡迎留言交流。
5.動态縮放
這個效果純粹是為了仿簡書的個人資訊的頭像的縮放效果。
頭像視圖的布局直接利用了navigationBar的titleView,代碼還是很好了解的,視圖層級關系如下:
頭像的一半是“懸空”的。
縮放隻是滑動代理監聽時的一個動态縮放的過程,縮放率同之前說的alpha的動态變化一樣,自行設定常量,這裡注意縮放過程的分段執行,為了效果,這個有下拉放大回彈的效果,這部分不是簡書的效果:
如果隻是設定縮放,效果如下:
為了使縮放過程中位置相對保持,之前想到了改變錨點的方式,然而效果很差,最終還是以簡單的方式實作:
CGRect frame = _topImageView.frame;
frame.origin.y = ;
_topImageView.frame = frame;
就是保持y的坐标為初始值。
下面的這個仿微網誌的效果,也是基于同樣的原理,隻是要注意圖層關系,詳細參考代碼,不再贅述。
6.側滑傳回
這算是個比較大的話題了,前面提到的那篇文章中有關于此的詳細論述,這裡隻是簡單提及,稍後有機會,會專門寫一篇關于側滑傳回的自定義實作。
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簡介