天天看點

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

前言

本文整理了一些關于navigationBar的非正常的但是較為實用的操作,包括利用毛玻璃、動态透明、動态item顔色、動态隐藏,以及頭視圖的動态縮放,并同時涉及了statusBar的動态設定(換色)。

先預覽下整體效果:

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

Demo詳見GitHub:Demo_AboutNavigationBar

為了便于展示,類沒有複用,也沒有繼承共有的父類,所有存在大量“有絲分裂”的重複代碼。。。說白了就是懶。。。千萬别學我就是了。

1.不要“浪費”了這塊毛玻璃

這裡所謂的不要浪費,隻是個人的偏好,當然也是順遂了蘋果的UI特色之一:毛玻璃穿透效果。

一般界面上是有兩塊毛玻璃的:navigationBar和tabBar,很多APP都會自定義這兩塊Bar,不僅是顔色,甚至是控件本身。單考慮利用系統的Bar的話,如果不自定義顔色,就是利用毛玻璃效果本身了,然而很多APP,也并未利用起這個毛玻璃效果,簡書的就是這樣,不過簡書應該是自定義了navigationBar?或許。

(這裡插一句,就是QQ的某次更新,更新之後,navigationBar的藍色變成了毛玻璃,之後的一次更新又換了回來……可能是被吐槽太多?這裡應該是使用者習慣先入為主的問題了;還有就是微信,微信應該算得上是個十分純粹的iOS風格的APP,有留意過的話,也會發現其navigationBar是黑色的毛玻璃,或許是

barStyle = UIBarStyleBlack

,因為微信也是有很多非原生的處理的。)

下面結合tableView介紹下其與navigationBar的毛玻璃的作用效果。知道我可能要啰嗦什麼的朋友可自行跳過這部分内容。

先對比下這兩種效果(圖不太清晰,仔細看navigationBar,還是很容易對比出來差别的):

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

有毛玻璃穿透效果的效果

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

沒有毛玻璃穿透效果

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

帶來的小問題

  • 1.webView初始加載時底部的黑條

    如果使用上述方式初始化webView(同樣是滑動視圖,有scrollView屬性,可設定

    scrollView.contentInset

    ),至于所謂的“黑條”,大緻類似于下面的效果:
    iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回
    黑條出現在視圖底部,一般高度為64,也就是

    contentInset

    top

    值,上圖的因為設定關系要大很多。相信很多人都在浏覽APP的webView時,在web加載完之前的空白頁時,見到過這一現象。

    關于這以現象的解決方法網上也有很多,我一般是在初始化時,将

    contentOffset.y

    設定一個(-64)的偏移量,這樣,初始進入webView時,在不滑動頁面的情況下,是不可見的,這算是較為溫和的解決方法吧,還有一種就是,初始化時設定

    opaque=NO

    ,也就是使得view透明,但是在加載之後,要設定

    opaque=YES

    ,也就是預設不透明,這樣,黑條是徹底不可見的。
  • 2.滑動偏移量改變

    在實作一些滑動視圖的動态變化的進行中,例如稍後提到的動态縮放,一般會用代理檢測

    contentOffset.y

    值,但是因為設定了

    contentInset.top

    ,此時

    contentOffset.y

    值也就發生了變化:
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        CGFloat offsetY = scrollView.contentOffset.y + self.tableView.contentInset.top;//注意
    }
               
    初始不再是0,而是-64,也就是

    -top

1.4.為什麼需要毛玻璃效果

費了一些功夫,都隻是為了實作毛玻璃效果,至于為什麼要這個毛玻璃穿透效果,我也說不好,喜好也罷,設計風格也罷,至少,感覺這樣做出的APP,不用專門考慮各種Bar的配色什麼的,動态模糊穿透的彩色,也的确很漂亮,至少,iOS的UI風格有這樣的趨勢。也不要小瞧這個毛玻璃效果,真正實作一個高效的毛玻璃,也就是動态模糊渲染,也是很麻煩的,一些三方庫,為了版本适配(适配iOS7),自定義的毛玻璃,也是利用了系統的toolBar。看過一些文章,介紹來介紹去,還是說,用系統在iOS8之後提供的API實作毛玻璃是最好的方式。

下圖(渣圖……)是我最近封裝了有一陣子的一個自定義控件,同樣是為了效果,用toolBar自定義了一塊毛玻璃,這個控件還未徹底完善,稍後會開源,下圖隻是其中一種樣式,這個控件的自定義程度還是非常高的。

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

2.navigationBar的透明

有時候,我們需要将navigationBar設定透明,但不是隐藏,因為還需要其item控件(傳回鍵什麼的),雖然navigationBar是繼承于UIView的,但是直接設定其

alpha

是無效的,應該是因為navigationBar複合的視圖層級:

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回
iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

根據視圖層級關系,我們用這個十分簡單的方法來設定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.第二場景,松手

    這個處理十分簡單:

    - (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];
        }
    }
               
    velocity.y這個量,在上滑和下滑時,變化極小(小數),但是因為方向不同,有正負之分,這就很好處理了。
  • 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,代碼還是很好了解的,視圖層級關系如下:

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

頭像的一半是“懸空”的。

縮放隻是滑動代理監聽時的一個動态縮放的過程,縮放率同之前說的alpha的動态變化一樣,自行設定常量,這裡注意縮放過程的分段執行,為了效果,這個有下拉放大回彈的效果,這部分不是簡書的效果:

如果隻是設定縮放,效果如下:

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

為了使縮放過程中位置相對保持,之前想到了改變錨點的方式,然而效果很差,最終還是以簡單的方式實作:

CGRect frame = _topImageView.frame;
frame.origin.y = ;
_topImageView.frame = frame;
           

就是保持y的坐标為初始值。

下面的這個仿微網誌的效果,也是基于同樣的原理,隻是要注意圖層關系,詳細參考代碼,不再贅述。

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

6.側滑傳回

這算是個比較大的話題了,前面提到的那篇文章中有關于此的詳細論述,這裡隻是簡單提及,稍後有機會,會專門寫一篇關于側滑傳回的自定義實作。

iOS7之後,navigationController推出的視圖,隻要傳回按鈕不自定義覆寫,或者相關屬性預設,右滑螢幕左邊緣,可以直接pop傳回,這是個十分友善的功能。關于實作和自定義實作,網上也有太多的文章。這裡隻是說明一點,就是在設定navigationBar透明之後,使用側滑傳回的過程中,navigationBar會突然出現,顯得十分突兀,這也是目前微網誌APP的效果,QQ和微信都針對此做了不同的處理,本文的Demo中也嘗試做了一定的處理,但是效果并不是十分理想的,就是中斷側滑傳回動作時,navigationBar閃的問題。這個問題已經超出本文内容的範圍,有機會會在今後的文章中介紹。

iOS 關于navigationBar的一些:毛玻璃、透明、動态縮放、動态隐藏前言1.不要“浪費”了這塊毛玻璃2.navigationBar的透明3.動态statusBar顔色4.navigationBar的動态隐藏5.動态縮放6.側滑傳回

參考文章:

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簡介