UINavigationController與UINavigationBar詳解

UINavigationController與UINavigationBar詳解.png

UINavigationController相關(guān)

概述

UINavigationController繼承自UIViewController, 專門用于展示層級(jí)結(jié)構(gòu)的內(nèi)容, 可以讓我們更高效地將層級(jí)結(jié)構(gòu)的內(nèi)容呈現(xiàn)給用戶. UINavigationController在展示界面時(shí)模擬數(shù)據(jù)的層級(jí)結(jié)構(gòu), 在每個(gè)層級(jí)分別使用一個(gè)UIViewController來(lái)展示該層級(jí)的內(nèi)容, 下面我們以iOS設(shè)備中的"設(shè)置"界面為例, 進(jìn)行一下簡(jiǎn)單的認(rèn)識(shí)

UINavigationController樣例.png

UINavigationController(導(dǎo)航控制器)是一個(gè)容器控制器, 其內(nèi)部展示著多個(gè)UIViewController(視圖控制器)的內(nèi)容, 我們可以通過UINavigationController的view屬性獲取到其自身的視圖, 在該視圖上有一個(gè)位于界面頂部的UINavigationBar(導(dǎo)航欄)和位于界面底部的默認(rèn)隱藏的UIToolbar(工具欄), 以及一個(gè)位于界面中間部分的UIViewController的view

UINavigationController層級(jí).png

當(dāng)用戶在UINavigationController的層級(jí)結(jié)構(gòu)中來(lái)回切換時(shí), UINavigationBar和UIToolbar的內(nèi)容會(huì)隨著發(fā)生變化, 但是其本身并不會(huì)發(fā)生變化, 唯一發(fā)生變化的就是位于界面中間部分的UIViewController的view

UIViewController堆棧的管理

UINavigationController通過其管理的UIViewController堆棧來(lái)決定展示在UINavigationController中間部位的內(nèi)容, 該內(nèi)容由位于UIViewController堆棧棧頂位置的UIViewController來(lái)決定

UIViewController堆棧.png

注: 上圖中viewControllers是UIViewController堆棧; 上圖中navigationBar是位于界面頂部的UINavigationBar; 上圖中toolbar是位于界面底部的UIToolbar

我們可以利用系統(tǒng)提供的方法向UIViewController堆棧中Push一個(gè)UIViewController, 從UIViewController堆棧中Pop一個(gè)UIViewController, 也可以直接設(shè)置UIViewController堆棧中的全部UIViewController, 下面將分別對(duì)其進(jìn)行介紹

注: 位于UIViewController堆棧棧底位置的UIViewController稱為"根視圖控制器", 每個(gè)UINavigationController在展示到屏幕上之前都必須擁有該根視圖控制器

向UIViewController堆棧中Push一個(gè)UIViewController
// 方法
/*
 * 參數(shù)一: UIViewController, 該參數(shù)不可以使用UITabBarController的實(shí)例
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated;
// 示例
[navigationController pushViewController:[[UIViewController alloc] init] animated:YES];
// 方法
/*
 * 參數(shù)一: UIViewController, 該參數(shù)不可以使用UITabBarController的實(shí)例
 * 參數(shù)二: 要求展示UIViewController的對(duì)象
 */
- (void)showViewController:(UIViewController *)vc sender:(id)sender;
// 示例
[navigationController showViewController:[[UIViewController alloc] init] sender:nil];

注: 該方法與調(diào)用pushViewController:animated:相同, 該方法自iOS8開始生效

從UIViewController堆棧中Pop一個(gè)UIViewController
// 方法
/*
 * 參數(shù)一: 是否執(zhí)行動(dòng)畫
 * 返回值: 從UIViewController堆棧中Pop出來(lái)的UIViewController
 */
- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
// 示例
UIViewController *viewController = [navigationController popViewControllerAnimated:YES];

注: 如果UIViewController堆棧中只有一個(gè)UIViewController, 則該方法無(wú)效

從UIViewController堆棧中一直Pop到根視圖控制器
// 方法
/*
 * 參數(shù)一: 是否執(zhí)行動(dòng)畫
 * 返回值: 從UIViewController堆棧中Pop出來(lái)的UIViewController數(shù)組
 */
- (NSArray<__kindof UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated;
// 示例
NSArray *viewControllers = [navigationController popToRootViewControllerAnimated:YES];
從UIViewController堆棧中一直Pop到指定UIViewController
// 方法
/*
 * 參數(shù)一: 指定UIViewController, 該UIViewController必須位于當(dāng)前UIViewController堆棧中
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 * 返回值: 從UIViewController堆棧中Pop出來(lái)的UIViewController數(shù)組
 */
- (NSArray<__kindof UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated;
// 示例
NSArray *viewControllers = [navigationController popToViewController:navigationController.viewControllers[0] animated:YES];
設(shè)置UIViewController堆棧中全部UIViewController
// 屬性
@property(nonatomic, copy) NSArray<__kindof UIViewController *> *viewControllers;
// 示例
NSArray *viewControllers = navigationController.viewControllers;

注: 設(shè)置該屬性等價(jià)于調(diào)用setViewControllers:animated:方法, 其中animated參數(shù)為NO

設(shè)置UIViewController堆棧中全部UIViewController(可選動(dòng)畫)
// 方法
/*
 * 參數(shù)一: UIViewController數(shù)組
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated;
// 示例
[navigationController setViewControllers:@[viewController1, viewController2, viewController3] animated:YES];

當(dāng)animated參數(shù)為YES時(shí), 系統(tǒng)會(huì)根據(jù)如下三種情況選擇采用何種動(dòng)畫形式

  • 當(dāng)數(shù)組中的最后一個(gè)元素在當(dāng)前堆棧中, 且是位于棧頂, 則采用無(wú)動(dòng)畫形式
  • 當(dāng)數(shù)組中的最后一個(gè)元素在當(dāng)前堆棧中, 且不是位于棧頂, 則采用Pop動(dòng)畫形式
  • 當(dāng)數(shù)組中的最后一個(gè)元素不在當(dāng)前堆棧中, 則采用Push動(dòng)畫形式
獲取位于UIViewController堆棧棧頂位置的UIViewController
// 屬性
@property(nonatomic, readonly, strong) UIViewController *topViewController;
// 示例
UIViewController *viewController = navigationController.topViewController;
獲取在UINavigationController中當(dāng)前可見視圖的UIViewController
// 屬性
@property(nonatomic, readonly, strong) UIViewController *visibleViewController;
// 示例
UIViewController *viewController = navigationController.visibleViewController;

注: 當(dāng)前可見視圖的UIViewController是位于UIViewController堆棧棧頂位置的UIViewController或者通過模態(tài)視圖展示出來(lái)的UIViewController

UINavigationBar的管理

UINavigationController負(fù)責(zé)創(chuàng)建、配置及管理UINavigationBar, 其通過位于UIViewController堆棧棧頂位置的UIViewController的navigationItem屬性(該屬性位于UIViewController的UINavigationControllerItem類目中)來(lái)管理UINavigationBar展示的內(nèi)容, 同時(shí)UINavigationController也提供了navigationBar屬性, 允許開發(fā)者通過該屬性設(shè)置UINavigationBar的外觀

注: UINavigationController是UINavigationBar的delegate, 其負(fù)責(zé)響應(yīng)該UINavigationBarDelegate的代理方法, 并據(jù)此更新位于界面中間部分的UIViewController的視圖

設(shè)置UINavigationBar的外觀
// 屬性
@property(nonatomic, readonly) UINavigationBar *navigationBar;
// 示例
navigationController.navigationBar.barStyle = UIBarStyleDefault;

注: 我們可以通過該屬性設(shè)置UINavigationBar的外觀, 但是不要通過該屬性設(shè)置其frame, bounds, alpha等屬性, 更不要修改其層級(jí)結(jié)構(gòu)

設(shè)置UINavigationBar的隱藏狀態(tài)
// 屬性
@property(nonatomic, getter=isNavigationBarHidden) BOOL navigationBarHidden;
// 示例
navigationController.navigationBarHidden = YES;

注: 設(shè)置該屬性等價(jià)于調(diào)用setNavigationBarHidden:animated:方法, 其中animated參數(shù)為NO

設(shè)置UINavigationBar的隱藏狀態(tài)(可選動(dòng)畫)
// 方法
/*
 * 參數(shù)一: 隱藏狀態(tài)
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated;
// 示例
[navigationController setNavigationBarHidden:YES animated:YES];

UIToolbar的管理

UINavigationController負(fù)責(zé)創(chuàng)建、配置及管理UIToolbar, 其通過位于UIViewController堆棧棧頂位置的UIViewController的toolbarItems屬性(該屬性位于UIViewController的UINavigationControllerContextualToolbarItems類目中)來(lái)管理UIToolbar展示的內(nèi)容, 同時(shí)UINavigationController也提供了toolbar屬性, 允許開發(fā)者通過該屬性設(shè)置UIToolbar的外觀

設(shè)置UIToolbar的外觀
// 屬性
@property(nonatomic, readonly) UIToolbar *toolbar;
// 示例
navigationController.toolbar.barStyle = UIBarStyleDefault;
設(shè)置UIToolbar的隱藏狀態(tài)
// 屬性
@property(nonatomic, getter=isToolbarHidden) BOOL toolbarHidden;
// 示例
navigationController.toolbarHidden = NO;

注: 設(shè)置該屬性等價(jià)于調(diào)用setToolbarHidden:animated:方法, 其中animated參數(shù)為NO

設(shè)置UIToolbar的隱藏狀態(tài)(可選動(dòng)畫)
// 方法
/*
 * 參數(shù)一: 隱藏狀態(tài)
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)setToolbarHidden:(BOOL)hidden animated:(BOOL)animated;
// 示例
[navigationController setToolbarHidden:NO animated:YES];

手勢(shì)識(shí)別器的管理

獲取手勢(shì)識(shí)別器
// 側(cè)滑返回手勢(shì)識(shí)別器
@property(nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer;
// 用于輕拍隱藏UINavigationBar與UIToolbar的手勢(shì)識(shí)別器
@property(nonatomic, readonly, assign) UITapGestureRecognizer *barHideOnTapGestureRecognizer;
// 用于輕掃隱藏UINavigationBar與UIToolbar的手勢(shì)識(shí)別器
@property(nonatomic, readonly, strong) UIPanGestureRecognizer *barHideOnSwipeGestureRecognizer;
// 示例
UIGestureRecognizer *interactivePopGestureRecognizer = navigationController.interactivePopGestureRecognizer;

注: 默認(rèn)情況下, 用戶的一個(gè)操作只能被一個(gè)UIGestureRecognizer(手勢(shì)識(shí)別器)識(shí)別, 系統(tǒng)將該手勢(shì)識(shí)別器提供給開發(fā)者以便在gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:代理方法中設(shè)置允許其他手勢(shì)識(shí)別器與該手勢(shì)識(shí)別器同時(shí)被識(shí)別

通過手勢(shì)隱藏UINavigationBar與UIToolbar
// 輕拍隱藏、再次輕拍顯示
@property(nonatomic, readwrite, assign) BOOL hidesBarsOnTap;
// 向上輕掃隱藏、向下輕掃顯示
@property(nonatomic, readwrite, assign) BOOL hidesBarsOnSwipe;
// 橫屏隱藏(此時(shí)輕拍顯示)、豎屏顯示
@property(nonatomic, readwrite, assign) BOOL hidesBarsWhenVerticallyCompact;
// 鍵盤出現(xiàn)隱藏、鍵盤消失保持隱藏(此時(shí)輕拍顯示)
@property(nonatomic, readwrite, assign) BOOL hidesBarsWhenKeyboardAppears;
// 示例
navigationController.hidesBarsOnTap = YES;

注: 該屬性自iOS8開始生效

UINavigationController對(duì)象的初始化

UINavigationController對(duì)象的初始化分為兩種方式, 下面將分別對(duì)其進(jìn)行介紹

第一種方式, 我們可以直接通過"根視圖控制器"來(lái)初始化, 該種方式會(huì)自動(dòng)向UIViewController堆棧中Push該UIViewController

// 方法
/*
 * 參數(shù)一: UIViewController, 該參數(shù)不可以使用UITabBarController的實(shí)例
 */
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
// 示例
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:[[UIViewController alloc] init]];

第二種方式, 我們可以通過UINavigationBar及UIToolbar來(lái)初始化, 該種方式需要我們?cè)赨INavigationController展示在屏幕上之前向UIViewController堆棧中Push一個(gè)或多個(gè)UIViewController

// 方法
/*
 * 參數(shù)一: 自定義UINavigationBar的子類, 如果是nil則為UINavigationBar類
 * 參數(shù)二: 自定義UIToolbar的子類, 如果是nil則為UIToolbar類
 */
- (instancetype)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass;
// 示例
UINavigationController *navigationController = [[UINavigationController alloc] initWithNavigationBarClass:[UINavigationBar class] toolbarClass:[UIToolbar class]];

注: 如果只是希望訂制全部UINavigationBar或UIToolbar的外觀, 可以優(yōu)先考慮使用UIAppearance API進(jìn)行全局設(shè)置, 而不是自定義其子類, 下文中屬性/方法后面標(biāo)有UI_APPEARANCE_SELECTOR的均可使用UIAppearance API進(jìn)行全局設(shè)置

UINavigationController的代理方法

將要展示一個(gè)UIViewController
// 方法
/*
 * 參數(shù)一: UINavigationController
 * 參數(shù)二: UIViewController
 * 參數(shù)三: 是否執(zhí)行動(dòng)畫
 */
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

注: 該UIViewController將要位于UIViewController堆棧棧頂位置(可能是通過Push/Pop/直接設(shè)置UIViewController堆棧的方式位于UIViewController堆棧棧頂位置)

已經(jīng)展示一個(gè)UIViewController
// 方法
/*
 * 參數(shù)一: UINavigationController
 * 參數(shù)二: UIViewController
 * 參數(shù)三: 是否執(zhí)行動(dòng)畫
 */
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

注: 該UIViewController已經(jīng)位于UIViewController堆棧棧頂位置(可能是通過Push/Pop/直接設(shè)置UIViewController堆棧的方式位于UIViewController堆棧棧頂位置)

提供自定義非交互式過渡動(dòng)畫
// 方法
/*
 * 參數(shù)一: UINavigationController
 * 參數(shù)二: UINavigationControllerOperation, 可選項(xiàng)為UINavigationControllerOperationNone(無(wú)操作), UINavigationControllerOperationPush(Push操作), UINavigationControllerOperationPop(Pop操作)
 * 參數(shù)三: UIViewController, 當(dāng)前可見視圖控制器
 * 參數(shù)四: UIViewController, 動(dòng)畫結(jié)束后可見視圖控制器
 * 返回值: 實(shí)現(xiàn)了UIViewControllerAnimatedTransitioning協(xié)議的NSObject對(duì)象
 */
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC;

注: 如果想要提供自定義交互式過渡動(dòng)畫, 需要額外實(shí)現(xiàn)navigationController:interactionControllerForAnimationController:代理方法

提供自定義交互式過渡動(dòng)畫
// 方法
/*
 * 參數(shù)一: UINavigationController
 * 參數(shù)二: 由navigationController:animationControllerForOperation:fromViewController:toViewController:代理方法提供的實(shí)現(xiàn)了UIViewControllerAnimatedTransitioning協(xié)議的NSObject對(duì)象
 * 返回值: 實(shí)現(xiàn)了UIViewControllerInteractiveTransitioning協(xié)議的NSObject對(duì)象
 */
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController;

注: 需要同時(shí)實(shí)現(xiàn)navigationController:animationControllerForOperation:fromViewController:toViewController:代理方法

提供優(yōu)先展示方向
// 方法
/*
 * 參數(shù)一: UINavigationController
 * 返回值: UIInterfaceOrientation
 */
- (UIInterfaceOrientation)navigationControllerPreferredInterfaceOrientationForPresentation:(UINavigationController *)navigationController;
提供支持展示方向
// 方法
/*
 * 參數(shù)一: UINavigationController
 * 返回值: UIInterfaceOrientationMask
 */
- (UIInterfaceOrientationMask)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController;

UINavigationBar相關(guān)

概述

UINavigationBar是一個(gè)在層級(jí)結(jié)構(gòu)中起導(dǎo)航作用的視覺控件, 其一般展示形式如下圖所示

UINavigationBar樣例.png

UINavigationItem堆棧的管理

UINavigationBar雖然繼承自UIView, 但是其并非通過addSubview:方法來(lái)添加子視圖, 而是通過其管理的UINavigationItem堆棧來(lái)決定展示在UINavigationBar中的內(nèi)容

UINavigationItem堆棧.png

注: 上圖中items是UINavigationItem堆棧; 上圖中topItem是位于UINavigationItem堆棧棧頂位置的UINavigationItem; 上圖中backItem是位于UINavigationItem堆棧棧頂下方位置的UINavigationItem

我們可以利用系統(tǒng)提供的方法向UINavigationItem堆棧中Push一個(gè)UINavigationItem, 從UINavigationItem堆棧中Pop一個(gè)UINavigationItem, 也可以直接設(shè)置UINavigationItem堆棧中的全部UINavigationItem, 下面將分別對(duì)其進(jìn)行介紹

向UINavigationItem堆棧中Push一個(gè)UINavigationItem
// 方法
/*
 * 參數(shù)一: UINavigationItem
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)pushNavigationItem:(UINavigationItem *)item animated:(BOOL)animated;
// 示例
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Owen"];
[navigationBar pushNavigationItem:navigationItem animated:YES];
從UINavigationItem堆棧中Pop一個(gè)UINavigationItem
// 方法
/*
 * 參數(shù)一: 是否執(zhí)行動(dòng)畫
 * 返回值: 從UINavigationItem堆棧中Pop出來(lái)的UINavigationItem
 */
- (UINavigationItem *)popNavigationItemAnimated:(BOOL)animated;
// 示例
UINavigationItem *navigationItem = [navigationBar popNavigationItemAnimated:YES];
設(shè)置UINavigationItem堆棧中全部UINavigationItem
// 屬性
@property(nonatomic, copy) NSArray<UINavigationItem *> *items;
// 示例
navigationBar.items = @[navigationItem1, navigationItem2, navigationItem3];

注: 設(shè)置該屬性等價(jià)于調(diào)用setViewControllers:animated:方法, 其中animated參數(shù)為NO

設(shè)置UINavigationItem堆棧中全部UINavigationItem(可選動(dòng)畫)
// 方法
/*
 * 參數(shù)一: UINavigationItem數(shù)組
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)setItems:(NSArray<UINavigationItem *> *)items animated:(BOOL)animated;
// 示例
[navigationBar setItems:@[navigationItem1, navigationItem2, navigationItem3] animated:YES];

當(dāng)animated參數(shù)為YES時(shí), 系統(tǒng)會(huì)根據(jù)如下三種情況選擇采用何種動(dòng)畫形式

  • 當(dāng)數(shù)組中的最后一個(gè)元素在當(dāng)前堆棧中, 且是位于棧頂, 則采用無(wú)動(dòng)畫形式
  • 當(dāng)數(shù)組中的最后一個(gè)元素在當(dāng)前堆棧中, 且不是位于棧頂, 則采用Pop動(dòng)畫形式
  • 當(dāng)數(shù)組中的最后一個(gè)元素不在當(dāng)前堆棧中, 則采用Push動(dòng)畫形式
獲取位于UINavigationItem堆棧棧頂位置的UINavigationItem
// 屬性
@property(nonatomic, readonly, strong) UINavigationItem *topItem;
// 示例
UINavigationItem *navigationItem = navigationBar.topItem;
獲取位于UINavigationItem堆棧棧頂下方位置的UINavigationItem
// 屬性
@property(nonatomic, readonly, strong) UINavigationItem *backItem;
// 示例
UINavigationItem *navigationItem = navigationBar.backItem;

注: 如果UINavigationItem堆棧中只有一個(gè)UINavigationItem, 則該值為nil

UINavigationBar的內(nèi)容

UINavigationBar通過UINavigationItem堆棧按照如下方式來(lái)決定展示在UINavigationBar中的內(nèi)容

位于中間的標(biāo)題會(huì)根據(jù)下方順序選擇展示的內(nèi)容

  • 如果topItem設(shè)置了標(biāo)題視圖(titleView屬性), 則展示標(biāo)題視圖
  • 如果topItem設(shè)置了標(biāo)題文字(title屬性), 則展示標(biāo)題文字
  • 如果以上都未設(shè)置, 則展示空白

位于右側(cè)的按鈕會(huì)根據(jù)下方順序選擇展示的內(nèi)容

  • 如果topItem設(shè)置了右側(cè)按鈕(rightBarButtonItem屬性), 則展示右側(cè)按鈕
  • 如果以上都未設(shè)置, 則展示空白

位于左側(cè)的按鈕會(huì)根據(jù)下方順序選擇展示的內(nèi)容

  • 如果topItem設(shè)置了左側(cè)按鈕(leftBarButtonItem屬性), 則展示左側(cè)按鈕
  • 如果backItem設(shè)置了返回按鈕(backBarButtonItem屬性), 則展示返回按鈕
  • 如果backItem設(shè)置了標(biāo)題文字(title屬性), 則展示利用標(biāo)題文字封裝的返回按鈕
  • 如果以上都未設(shè)置, 則展示利用文字"Back"封裝的返回按鈕

注: 如果UINavigationItem堆棧中只有一個(gè)UINavigationItem, 則不會(huì)展示返回按鈕

初始化UINavigationItem對(duì)象
// 方法
/*
 * 參數(shù)一: 標(biāo)題文字
 */
- (instancetype)initWithTitle:(NSString *)title;
// 示例
UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:@"Owen"];
設(shè)置標(biāo)題文字
// 屬性
@property(nonatomic, copy) NSString *title;
// 示例
navigationItem.title = @"Title";
設(shè)置標(biāo)題視圖
// 屬性
@property(nonatomic, strong) UIView *titleView;
// 示例
navigationItem.titleView = [UIButton buttonWithType:UIButtonTypeInfoLight];
設(shè)置提示文字
// 屬性
@property(nonatomic, copy) NSString *prompt;
// 示例
navigationItem.title = @"Prompt";
設(shè)置返回按鈕
// 屬性
@property(nonatomic, strong) UIBarButtonItem *backBarButtonItem;
// 示例
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Owen" style:UIBarButtonItemStylePlain target:nil action:nil];
navigationItem.backBarButtonItem = barButtonItem;

注: 該UIBarButtonItem對(duì)象需要是通過title或image創(chuàng)建的, 如果是通過自定義視圖創(chuàng)建的UIBarButtonItem對(duì)象在此不生效

設(shè)置返回按鈕隱藏狀態(tài)
// 屬性
@property(nonatomic, assign) BOOL hidesBackButton;
// 示例
navigationItem.hidesBackButton = YES;
設(shè)置返回按鈕隱藏狀態(tài)(可選動(dòng)畫)
// 方法
/*
 * 參數(shù)一: 是否隱藏返回按鈕
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)setHidesBackButton:(BOOL)hidesBackButton animated:(BOOL)animated;
// 示例
[navigationItem setHidesBackButton:YES animated:YES];
設(shè)置左/右側(cè)按鈕
// 屬性
@property(nonatomic, strong) UIBarButtonItem *leftBarButtonItem;
@property(nonatomic, copy) NSArray<UIBarButtonItem *> *leftBarButtonItems;
@property(nonatomic, strong) UIBarButtonItem *rightBarButtonItem;
@property(nonatomic, copy) NSArray<UIBarButtonItem *> *rightBarButtonItems;
// 示例
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:nil action:nil];
navigationItem.rightBarButtonItem = barButtonItem;
設(shè)置左/右側(cè)按鈕(可選動(dòng)畫)
// 方法
/*
 * 參數(shù)一: UIBarButtonItem
 * 參數(shù)二: 是否執(zhí)行動(dòng)畫
 */
- (void)setLeftBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated;
- (void)setLeftBarButtonItems:(NSArray<UIBarButtonItem *> *)items animated:(BOOL)animated;
- (void)setRightBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated;
- (void)setRightBarButtonItem:(UIBarButtonItem *)item animated:(BOOL)animated;
// 示例
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:nil action:nil];
[navigationItem setRightBarButtonItem:barButtonItem animated:YES];
設(shè)置左側(cè)按鈕是否與返回按鈕共同存在
屬性
@property(nonatomic) BOOL leftItemsSupplementBackButton;
// 示例
navigationItem.leftItemsSupplementBackButton = YES;

注: 該屬性默認(rèn)為NO, 即左側(cè)按鈕會(huì)替代返回按鈕而單獨(dú)存在, 如果設(shè)置為YES, 則左側(cè)按鈕會(huì)與返回按鈕共同存在并顯示在返回按鈕的右側(cè)

UINavigationBar的外觀

UINavigationBar類中提供了大量屬性/方法用于設(shè)置其外觀, 我們可以設(shè)置其樣式、背景顏色、色彩顏色、文字屬性等

UINavigationBar的外觀1.png

同樣, 我們也可以設(shè)置其背景圖片、陰影圖片等

UINavigationBar的外觀2.png
設(shè)置樣式
// 屬性
@property(nonatomic,assign) UIBarStyle barStyle UI_APPEARANCE_SELECTOR;
// 示例
navigationBar.barStyle = UIBarStyleDefault;

注: 該屬性默認(rèn)為UIBarStyleDefault(白底黑字), 可選項(xiàng)為UIBarStyleBlack(黑底白字)

設(shè)置透明性
// 屬性
@property(nonatomic, assign, getter=isTranslucent) BOOL translucent UI_APPEARANCE_SELECTOR;
// 示例
navigationBar.translucent = YES;

該屬性默認(rèn)為YES

  • 當(dāng)為UINavigationBar設(shè)置了背景圖片時(shí), 如果圖片中有任何像素的alpha小于1.0, 則該屬性為YES, 否則為NO
  • 當(dāng)為UINavigationBar設(shè)置了不透明的背景圖片時(shí), 如果設(shè)置屬性為YES, 則該圖片會(huì)被做透明處理
  • 當(dāng)為UINavigationBar設(shè)置了透明的背景圖片時(shí), 如果設(shè)置屬性為NO, 則該圖片會(huì)被添加一個(gè)不透明的背景, 該背景在UIBarStyleDefault樣式下為白色, 在UIBarStyleBlack樣式下為黑色, 如果設(shè)置了barTintColor, 則與其同色
設(shè)置背景顏色
// 屬性
@property(nonatomic, strong) UIColor *barTintColor UI_APPEARANCE_SELECTOR;
// 示例
navigationBar.barTintColor = [UIColor orangeColor];
設(shè)置色彩顏色
// 屬性
@property(nonatomic, strong) UIColor *tintColor;
// 示例
navigationBar.tintColor = [UIColor greenColor];
設(shè)置標(biāo)題文字屬性
// 屬性
@property(nonatomic, copy) NSDictionary<NSString *,id> *titleTextAttributes UI_APPEARANCE_SELECTOR;
// 示例
navigationBar.titleTextAttributes = @{NSFontAttributeName: [UIFont systemFontOfSize:30], NSForegroundColorAttributeName: [UIColor whiteColor]};
設(shè)置/獲取標(biāo)題欄豎直位置偏移
// 方法
/*
 * 參數(shù)一: 偏移量
 * 參數(shù)二: 可選項(xiàng)為UIBarMetricsDefault(豎屏), UIBarMetricsCompact(橫屏), UIBarMetricsDefaultPrompt(擁有提示文字的豎屏), UIBarMetricsCompactPrompt(擁有提示文字的橫屏)
 */
- (void)setTitleVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics UI_APPEARANCE_SELECTOR;
- (CGFloat)titleVerticalPositionAdjustmentForBarMetrics:(UIBarMetrics)barMetrics UI_APPEARANCE_SELECTOR;
// 示例
[navigationBar setTitleVerticalPositionAdjustment:10.0f forBarMetrics:UIBarMetricsDefault];
設(shè)置/獲取背景圖片
// 方法
/*
 * 參數(shù)一: 背景圖片
 * 參數(shù)二: 默認(rèn)為UIBarPositionAny(不指定), 可選項(xiàng)為UIBarPositionBottom(在容器下方), UIBarPositionTop(在容器上方), UIBarPositionTopAttached(在屏幕上方, 與容器平級(jí))
 * 參數(shù)三: 可選項(xiàng)為UIBarMetricsDefault(豎屏, 橫屏未設(shè)置也使用該效果), UIBarMetricsCompact(橫屏), UIBarMetricsDefaultPrompt(擁有提示文字的豎屏, 橫屏未設(shè)置也使用該效果), UIBarMetricsCompactPrompt(擁有提示文字的橫屏)
 */
- (void)setBackgroundImage:(UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics  UI_APPEARANCE_SELECTOR;
- (void)setBackgroundImage:(UIImage *)backgroundImage forBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics  UI_APPEARANCE_SELECTOR;
- (UIImage *)backgroundImageForBarMetrics:(UIBarMetrics)barMetrics  UI_APPEARANCE_SELECTOR;
- (UIImage *)backgroundImageForBarPosition:(UIBarPosition)barPosition barMetrics:(UIBarMetrics)barMetrics  UI_APPEARANCE_SELECTOR;
// 示例
[navigationBar setBackgroundImage:[UIImage imageNamed:@""] forBarPosition:UIBarPositionTop barMetrics:UIBarMetricsDefault];
設(shè)置陰影圖片
// 屬性
@property(nonatomic, strong) UIImage *shadowImage UI_APPEARANCE_SELECTOR;
// 示例
navigationBar.shadowImage = [UIImage imageNamed:@""];

注: 該陰影圖片默認(rèn)為位于UINavigationBar最下方的一條黑線, 在設(shè)置該屬性之前, 需要通過setBackgroundImage:forBarMetrics:設(shè)置一個(gè)背景圖片方可生效

注: 我們可以通過設(shè)置該屬性為[UIImage new]去除默認(rèn)陰影圖片

設(shè)置返回按鈕圖片
// 屬性
@property(nonatomic, strong) UIImage *backIndicatorImage UI_APPEARANCE_SELECTOR;
@property(nonatomic, strong) UIImage *backIndicatorTransitionMaskImage UI_APPEARANCE_SELECTOR;
// 示例
navigationBar.backIndicatorImage = [UIImage imageNamed:@""];
navigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@""];

注: 以上兩個(gè)屬性需要同時(shí)設(shè)置方可生效

UINavigationBar的代理方法

是否允許向UINavigationItem堆棧中Push一個(gè)UINavigationItem
// 方法
/*
 * 參數(shù)一: UINavigationBar
 * 參數(shù)二: UINavigationItem
 */
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item;
已經(jīng)向UINavigationItem堆棧中Push一個(gè)UINavigationItem
// 方法
/*
 * 參數(shù)一: UINavigationBar
 * 參數(shù)二: UINavigationItem
 */
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item;

注: 如果在Push的過程中有動(dòng)畫效果, 則該代理方法在動(dòng)畫結(jié)束后調(diào)用

是否允許從UINavigationItem堆棧中Pop一個(gè)UINavigationItem
// 方法
/*
 * 參數(shù)一: UINavigationBar
 * 參數(shù)二: UINavigationItem
 */
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
已經(jīng)從UINavigationItem堆棧中Pop一個(gè)UINavigationItem
// 方法
/*
 * 參數(shù)一: UINavigationBar
 * 參數(shù)二: UINavigationItem
 */
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;

注: 如果在Pop的過程中有動(dòng)畫效果, 則該代理方法在動(dòng)畫結(jié)束后調(diào)用

附錄A

在UINavigationController界面中, 如果前一個(gè)界面的UINavigationBar處于隱藏狀態(tài), 而后一個(gè)界面的UINavigationBar處于顯示狀態(tài), 我們經(jīng)常的處理方式可能是在前一個(gè)界面的UIViewController中寫入如下代碼

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    [self.navigationController setNavigationBarHidden:YES];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    
    [self.navigationController setNavigationBarHidden:NO];
}
附錄A.1.gif

利用上述方法得到的過渡結(jié)果并不盡如人意, 為了不讓UINavigationBar發(fā)生閃動(dòng), 我們只需在設(shè)置隱藏狀態(tài)時(shí)利用setNavigationBarHidden:animated:方法來(lái)設(shè)置即可

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    [self.navigationController setNavigationBarHidden:YES animated:YES];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    
    [self.navigationController setNavigationBarHidden:NO animated:YES];
}
附錄A.2.gif

附錄B

自iOS7開始, 系統(tǒng)在UINavigationController中提供了"側(cè)滑返回"功能, 即通過沿著屏幕側(cè)面滑動(dòng)可以進(jìn)行Pop操作, 下面將對(duì)其實(shí)現(xiàn)過程進(jìn)行分析

// 側(cè)滑返回手勢(shì)識(shí)別器
@property(nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer;

首先, 我們打印該屬性, 通過觀察我們可以知道該手勢(shì)識(shí)別器為UIScreenEdgePanGestureRecognizer(繼承自UIPanGestureRecognizer)對(duì)象, 且其target為_UINavigationInteractiveTransition對(duì)象、action為handleNavigationTransition:方法

其次, 我們打印該屬性的delegate, 通過觀察我們可以知道該delegate與上文target地址相同, 即二者為同一個(gè)對(duì)象

最終, 通過分析我們可以知道, 當(dāng)用戶觸發(fā)"側(cè)滑返回"手勢(shì)時(shí), 該手勢(shì)識(shí)別器就是調(diào)用了其delegate的handleNavigationTransition:方法來(lái)進(jìn)行過渡處理

// interactivePopGestureRecognizer = <UIScreenEdgePanGestureRecognizer: 0x7fb1c17702f0; state = Possible; enabled = NO; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7fb1c1646280>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fb1c176fd40>)>>

// interactivePopGestureRecognizer.delegate = <_UINavigationInteractiveTransition: 0x7fb1c176fd40>

隨著iOS設(shè)備屏幕越來(lái)越大, 側(cè)滑返回對(duì)于手小的用戶來(lái)說(shuō)已經(jīng)有些不方便, 這時(shí)便出現(xiàn)了全屏范圍內(nèi)滑動(dòng)返回的需求, 該功能已經(jīng)普遍用于網(wǎng)易新聞、手機(jī)QQ、新浪微博等大型APP中, 下面我們將進(jìn)行該功能的實(shí)現(xiàn)

首先, 我們自定義UINavigationController的子類, 在該子類中獲取到系統(tǒng)側(cè)滑返回手勢(shì)的信息

// 側(cè)滑返回所在View
UIView *view = self.interactivePopGestureRecognizer.view;
// 側(cè)滑返回target
id target = self.interactivePopGestureRecognizer.delegate;
// 側(cè)滑返回action
SEL action = NSSelectorFromString(@"handleNavigationTransition:");

其次, 我們關(guān)閉系統(tǒng)側(cè)滑返回手勢(shì)識(shí)別器, 同時(shí)創(chuàng)建一個(gè)全屏側(cè)滑手勢(shì)識(shí)別器, 且其屬性設(shè)置與interactivePopGestureRecognizer相同

self.interactivePopGestureRecognizer.enabled = NO;

UIPanGestureRecognizer *fullInteractivePopGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:target action:action];
fullInteractivePopGestureRecognizer.delaysTouchesBegan = YES;

最終, 我們模仿系統(tǒng)實(shí)現(xiàn)方法, 完整代碼如下所示

@interface LPNavigationController : UINavigationController

@property(nullable, nonatomic, readonly) UIGestureRecognizer *fullInteractivePopGestureRecognizer;

@end

@interface LPNavigationController () <UIGestureRecognizerDelegate>

@property(nullable, nonatomic, readwrite) UIGestureRecognizer *fullInteractivePopGestureRecognizer;

@end

@implementation LPNavigationController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.interactivePopGestureRecognizer.enabled = NO;
    
    UIView *view = self.interactivePopGestureRecognizer.view;
    id target = self.interactivePopGestureRecognizer.delegate;
    SEL action = NSSelectorFromString(@"handleNavigationTransition:");
    
    self.fullInteractivePopGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:target action:action];
    self.fullInteractivePopGestureRecognizer.delaysTouchesBegan = YES;
    self.fullInteractivePopGestureRecognizer.delegate = self;
    [view addGestureRecognizer:self.fullInteractivePopGestureRecognizer];
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    // 左滑時(shí)可能與UITableView左滑刪除手勢(shì)產(chǎn)生沖突
    CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:gestureRecognizer.view];
    if (translation.x <= 0)
    {
        return NO;
    }
    
    // 跟視圖控制器不響應(yīng)手勢(shì)
    return ([self.viewControllers count] == 1) ? NO : YES;
}

@end
附錄B.1.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容