1. 程式人生 > IOS開發 >iOS UINavigationController基本使用

iOS UINavigationController基本使用

UINavigationController是iOS提供的棧檢視控制器,它必須設定一個RootViewController根控制器,頁面跳轉時,通過它將下一個子ViewController的檢視新增到RootViewController的檢視中。

在Android中,可以聯想到Activity和Fragment,它們都使用了棧來管理檢視,而UINavigationController更加類似於Fragment,因為Activity之間的跳轉是2個Window之前的切換,前頁面佈局和後頁面的佈局是沒有關聯的,而Fragment和UINavigationController一樣,Fragment和Fragment之間的巢狀,是同一個Window下的佈局檢視之間的巢狀。

但Fragment的Bug和坑太多了,例如點選穿透,記憶體重啟後重影,Fragment彈棧到根Fragment並不能清除根以上的Fragment例項等。所以一般Fragment不做棧跳轉,而是內嵌到Activity和Fragment之間巢狀來使用。

UINavigationController示例.png

本篇來介紹一下UINavigationController的3個方面:

  1. UINavigationController建立和基本配置
  2. UINavigationBar導航欄樣式
  3. UINavigationController的棧管理API
  4. UIToolbar底部工具欄樣式

UINavigationController建立和基本配置

本篇都是用純程式碼方式,不使用Storyboard,所以記得在info.plist檔案中刪除掉MainStoryboard的設定。

  • UINavigationController使用alloc和initWithRootViewController來建立和初始化
//建立根檢視控制器
ViewController* rootVC = [[ViewController alloc] init];
UINavigationController* navVC = [[UINavigationController alloc] initWithRootViewController:rootVC];
複製程式碼
  • AppDelegate中,建立UINavigationController,將ViewController設定為UINavigationController的根檢視控制器,再將UINavigationController設定為Window的根控制器
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
    //建立根檢視控制器
    ViewController* rootVC = [[ViewController alloc] init];
    //建立UINavigationController,將根檢視控制器作為它的根檢視
    UINavigationController* navVC = [[UINavigationController alloc] initWithRootViewController:rootVC];
    //設定window的根檢視控制器為UINavigationController
    self.window.rootViewController = navVC;
    //顯示Window
    [self.window makeKeyAndVisible];
    return YES;
}
複製程式碼

UINavigationBar導航欄樣式

經過上面的建立和配置,我們的ViewController檢視控制器已經被UINavigationController控制了,我們會發現ViewController的檢視上有一個導航欄,它就是UINavigationBar。

UINavigationBar是由UINavigationController管理的,但是它的樣式由子控制器的self. navigationItem來設定。下面來說一下它的樣式配置和按鈕的新增。

  • 設定導航欄標題,預設取檢視控制器的self.title屬性,如果沒有設定則取self.navigationItem.title屬性。
//設定導航欄標題
self.title = @"根檢視";
//設定導航元素專案的標題,如果沒有設定self.navigationItem.title,系統會使用self.title作為導航欄的標題
//self.navigationItem.title = @"我也是標題";
複製程式碼
  • 導航欄透明和不透明設定

UINavigationBar導航欄預設是透明的,控制器檢視從裝置的左上角(0,0)點開始計算,而UINavigationBar覆蓋在控制器檢視以上,我們可以設定它為不透明,不透明時,控制器檢視從導航欄之下開始計算。

//設定導航欄是否透明,預設為YES: 透明,NO則為不透明
//如果不設定該屬性,導航欄和檢視控制器的View會重合,形成半透明
self.navigationController.navigationBar.translucent = NO;
複製程式碼
  • 導航欄風格設定,預設導航欄使用白底黑字的預設風格,還可以設定為黑底白字的黑色風格。設定方法有2種,第一種會將所有的UINavigationController都統一設定,第二種則只在呼叫的UINavigationController上設定。
//設定導航欄風格
//UIBarStyleDefault: 預設風格
//UIBarStyleBlack:黑色風格

//第一種,將所有的UINavigationController都統一設定(UINavigationController可以有多個)
[[UINavigationBar appearance] setBarStyle:UIBarStyleBlack];

//第二種,只在呼叫的UINavigationController上設定
self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
複製程式碼
  • 設定導航欄的顏色,設定該項會將狀態列設定為不透明,例如設定為紅色
//設定導航欄顏色,該屬性會將上面的透明屬性和BarStyle進行覆蓋
self.navigationController.navigationBar.barTintColor = [UIColor redColor];
複製程式碼
  • 設定導航欄上的元素(例如按鈕、文字的風格,例如文字的顏色會因風格顏色而改變字型顏色)
//設定導航欄上的元素顏色風格,例如影響導航欄上的文字顏色
self.navigationController.navigationBar.tintColor = [UIColor orangeColor];
複製程式碼
  • 隱藏UINavigationBar導航欄,有2種方式,第一種是UIView上的屬性,第二種是UINavigationController的屬性。
//隱藏導航欄,設定為YES則隱藏掉導航欄,這個屬性是UIView上面的屬性
self.navigationController.navigationBar.hidden = YES;
//或者導航控制器上的屬性也可以隱藏
self.navigationController.navigationBarHidden = YES;
複製程式碼

UINavigationBar導航欄新增按鈕

導航欄除了標題外,還可以擺放元素,常見就是新增操作按鈕和返回按鈕,也可以放置自定義的元素。

導航欄上的按鈕需要使用UIBarButtonItem,來包裹我們自定義內容或者自定義檢視。

按鈕風格可以分為3種,文字按鈕、系統圖片按鈕和自定義檢視,文字按鈕通過initWithTitle來設定,系統圖片按鈕則是通過initWithBarButtonSystemItem,最後自定義檢視使用initWithCustomView。

  • 以文字風格,設定左側按鈕,預設左側按鈕是一個返回鍵,我們可以覆蓋它,並設定按鈕事件,例如點選事件。
//建立一個導航欄左側按鈕
//引數一:按鈕標題
//引數二:按鈕風格
//引數三:事件擁有者
//引數四:按鈕事件
UIBarButtonItem* leftBtn = [[UIBarButtonItem alloc] initWithTitle:@"編輯" style:UIBarButtonItemStyleDone target:self action:@selector(pressLeft:)];
//設定到導航欄上
self.navigationItem.leftBarButtonItem = leftBtn;

/**
 * 左側按鈕點選事件回撥方法
 */
- (void) pressLeft:(UIBarButtonItem*)btn {
    NSLog(@"左側按鈕被按下");
}
複製程式碼
  • 以系統圖片風格,設定右側按鈕。
//建立一個導航欄右側按鈕,使用系統風格來建立,無需標題文字,因為它不能被改變
UIBarButtonItem* rightBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(pressRight:)];
//設定到導航欄上
self.navigationItem.rightBarButtonItem = rightBtn;

/**
 * 右側按鈕點選事件回撥方法
 */
- (void) pressRight:(UIBarButtonItem*)btn {
    NSLog(@"右側按鈕被按下");
}
複製程式碼
  • 以自定義View方式,設定右側按鈕,我們需要使用UIBarButtonItem的initWithCustomView方法將自定義View包裹成UIBarButtonItem來設定。
UILabel* label = [[UILabel alloc] initWithFrame:CGRectMake(10,10,40,40)];
label.text = @"測試";
label.textColor = [UIColor grayColor];
label.textAlignment = NSTextAlignmentCenter;
//將Label包裝為UIBarButtonItem
UIBarButtonItem* labelItem = [[UIBarButtonItem alloc] initWithCustomView:label];
複製程式碼
  • 還支援設定多個按鈕,例如我們將上面的自定義按鈕和系統風格按鈕放到一個陣列,再一起設定到右側。
//省略上面系統圖片風格按鈕和自定義View按鈕的建立和初始化...

//將多個按鈕放置到NSArray陣列中,再新增到導航欄的位置
NSArray* arrays = [NSArray arrayWithObjects: rightBtn,labelItem,nil];
//設定多個按鈕到導航欄上
self.navigationItem.rightBarButtonItems = arrays;
複製程式碼
  • 左側返回按鈕定製

預設子控制器的導航欄左側的是返回鍵按鈕,如果需要改變它,有2種方式,第一種就是上面說到的使用self.navigationItem.leftBarButtonItem來設定,第二種是設定self.navigationItem.backBarButtonItem屬性。leftBarButtonItem的優先順序大於backBarButtonItem。

  1. 第二種,在跳轉前的控制器中設定self.navigationItem.backBarButtonItem屬性,如果設定了,下一個控制器頁面的返回鍵則使用backBarButtonItem屬性設定的。
//設定下個頁面的返回按鈕
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"首頁" style:(UIBarButtonItemStyleDone) target:nil action:@selector(backToMy)];

/**
 * 返回當前控制器
 */
- (void) backToMy {
    [self.navigationController popToViewController:self animated:YES];
}
複製程式碼

UINavigationController的棧管理API

除了上面的設定按鈕,UINavigationController最重要的還是棧容器管理檢視控制器,下面來瞭解一下棧方面的Api。

  • 入棧下一個檢視控制器

首先先建立下一個控制器SecondViewController的例項,再通過控制器上的self.navigationController,呼叫pushViewController方法,將SecondViewController推入棧中,第二個animated引數代表是否需要跳轉動畫。

//建立下一個頁面的ViewController
SecondViewController* nextVC = [[SecondViewController alloc] init];
//跳轉到下一個頁面
[self.navigationController pushViewController:nextVC animated:YES];
複製程式碼
  • 入棧多少檢視控制器,傳入一個控制器陣列,跳轉後會顯示最後一個控制器
//建立2個檢視控制器
OneViewController *oneVC = [[OneViewController alloc] init];
TwoViewController *twoVC = [[TwoViewController alloc] init];
//將2個檢視控制器放到陣列中
NSArray *vcArray = [[NSArray alloc] initWithObjects:oneVC,twoVC,nil];
[self.navigationController setViewControllers:vcArray animated:YES];
複製程式碼
  • 出棧檢視控制器自身
[self.navigationController popViewControllerAnimated:YES];
複製程式碼
  • 出棧到指定控制器,會將指定控制器以上的控制器都進行出棧
[self.navigationController popToViewController:targetVC animated:YES];
複製程式碼

UIToolbar底部工具欄樣式

UINavigationController還附帶一個工具欄,預設是隱藏的,而且也比較少用

  • 顯示工具欄
//是否隱藏工具欄,預設為YES,預設隱藏
self.navigationController.toolbarHidden = NO;
複製程式碼
  • 設定工具欄透明和不透明
//設定工具欄透明度為透明
self.navigationController.toolbar.translucent = NO;
複製程式碼
  • 配置工具欄按鈕,按鈕也是使用UIBarButtonItem,和導航欄按鈕的建立方式一樣
//建立2個工具欄按鈕
UIBarButtonItem* btn01 = [[UIBarButtonItem alloc] initWithTitle:@"點贊" style:UIBarButtonItemStyleDone target:nil action:nil];
UIBarButtonItem* btn02 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:nil action:nil];
//將按鈕放到陣列裡
NSArray* toolBtns = [NSArray arrayWithObjects:btn01,btn02,nil];
//新增到工具欄
self.toolbarItems = toolBtns;
複製程式碼
  • 平分按鈕,上面新增2個按鈕是不會平分工具欄控制元件的,而是都靠左排列,如果想讓按鈕平分工具欄控制元件控制元件,則需要使用UIBarButtonSystemItemFlexibleSpace的風格建立UIBarButtonItem,按位置新增在陣列中即可,不用建立多個按鈕例項,一個即可。
//建立2個工具欄按鈕
UIBarButtonItem* btn01 = [[UIBarButtonItem alloc] initWithTitle:@"點贊" style:UIBarButtonItemStyleDone target:nil action:nil];
UIBarButtonItem* btn02 = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:nil action:nil];

//-------------- 自動平分按鈕(重點在這裡!!!) --------------
UIBarButtonItem* btnSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];

//將按鈕放到陣列裡
NSArray* toolBtns = [NSArray arrayWithObjects:btnSpace,btn01,btnSpace,nil];
//新增到工具欄
self.toolbarItems = toolBtns;
複製程式碼