iOS Swift5從0到1系列(三):學習UINavigationController(1)

一、前言

上篇主儡,我們仿了京東的底部導航欄奖唯,顯示了5個頁面(UIViewController),它的作用你可以理解為頁面之間的切換糜值,每個頁面都屬于一級頁面丰捷;而一級頁面一般只是一些吸引用戶的的主要流量入口,實際的功能頁面都是通過這些一級頁面的入口導航到下一級或是更深一級的頁面(從用戶角度來看寂汇,核心重要的頁面最多不超過三級病往,否則層級太深,用戶可能就沒興趣繼續(xù)點下去了骄瓣,除了『確認訂單』和『支付』頁面)停巷。

N級頁面之間的切換,不會再涉及到底部導航控制器榕栏,它沒那功能畔勤;在 iOS 中,具體頁面間導航功能的就是我們今天要學習的 UINavigationController扒磁,即導航控制器庆揪,它能夠實現(xiàn)不同頁面之間的進入與退出。

二妨托、導航控制器(UINavigationController)

導航控制器就是負責頁面切換的缸榛,同樣,我們先來看看官方源碼注釋:

UINavigationController manages a stack of view controllers and a navigation bar.
導航控制器通過數(shù)據(jù)結構『椑忌耍』的方式來管理一組 ViewController内颗,并且它自帶導航欄。

It performs horizontal view transitions for pushed and popped views while keeping the navigation bar in sync.
默認情況下敦腔,push 操作(進入下一級頁面)和 pop 操作(返回到上一級頁面)的轉場動畫是一個平移均澳,并且,導航控制器會保持導航欄(狀態(tài)的)同步会烙。

如果你覺得上面的注釋看不懂也沒關系负懦,接下來我會分析筒捺,知道的朋友可以直接跳下一小節(jié)(不太建議柏腻,有些細節(jié)『老司機』也不一定掌握 -_-|||)。我們先來看看 UIKit.UINavigationController 源碼系吭,了解其內部的核心對象和方法五嫂。

2.1、UINavigationController 分析

精華注釋.....

@available(iOS 2.0, *)
open class UINavigationController : UIViewController {
    // 構造器,需要一個 UIViewController 作為根視圖控制器
    // 初始化時沃缘,它會將這個 UIViewController 以 push 且沒有動畫的方式放入棧中
    // 該 UIViewController 將是棧中第一個視圖控制器
    public init(rootViewController: UIViewController) 
    
    // 默認使用平移動畫進入下一個頁面躯枢,如果當前的頁面已在棧頂,則沒有任何效果
    open func pushViewController(_ viewController: UIViewController, animated: Bool) 

    // 返回頁面有三種方式:
    // 1. 返回上一級頁面:即從哪里來的槐臀,回到哪里去
    open func popViewController(animated: Bool) -> UIViewController? 
    // 2. 返回到指定的頁面:你可以使用該方法返回上一級面頁锄蹂,也可以直接返回到根頁面
    // 對于返回到指定的頁面,則該頁面所在棧中之上的所有頁面都將出棧并銷毀(這些頁面的出棧不可見)
    open func popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]? 
    // 3. 返回到第一個頁面
    open func popToRootViewController(animated: Bool) -> [UIViewController]? 
    
    // 當前棧中存在的頁面水慨,我們可以通過遍歷:
    // 1. pop操作來返回到我們想要返回的頁面
    // 2. 通過個數(shù)來做一些判斷操作(后面會用到)
    open var viewControllers: [UIViewController] 
    
    @available(iOS 7.0, *)
    // 這個沒有英文注釋得糜,不過,我們都會用到
    // 我們也看到了它的出現(xiàn)是在 7.0 以后晰洒,而它的功能就是:(右)側滑返回上一級而面的手勢
    // 因為蘋果手機屏幕越來越大了嘛朝抖,總是讓用戶點『返回』按鈕來返回到上一級,體驗不好嘛
    open var interactivePopGestureRecognizer: UIGestureRecognizer? { get }
    
    // 導航欄谍珊,管理 navigationItem治宣,同樣使用棧來管理(為啥?)
    // 因為每個 VC 都對應著一個 navigationItem
    open var navigationBar: UINavigationBar { get } 
    
    @available(iOS 3.0, *)
    // tool條砌滞,含有 title 和 image
    open var toolbar: UIToolbar! { get }
    
    // 其它請自行查看
    ......
}

以上列出的方法和成員都是我們將來會用的著的侮邀,且會經(jīng)常打交道,不過贝润,類似于 UITabBarController豌拙,基本上只會打交道一次,為啥题暖?因為一般我們都會在封裝在基類中按傅,由基類來統(tǒng)一管理,如果每個頁面自己管理胧卤,則很容易亂套(蘋果自己對這塊都是非澄ㄉ埽混亂),而我給大家分享的枝誊,都是通過實踐來告訴大家避免踩坑的最終結果况芒。

2.2、擴展 UINavigationController 分析

extension UIViewController {
    // 大家可以自行創(chuàng)建導航欄的外觀(左叶撒、右按鈕绝骚,標題,文字祠够、圖片压汪、顏色和透明度)
    open var navigationItem: UINavigationItem { get } 
    // 這個屬性默認是 Swift: false / OC: NO,但這個屬性在遇到 UITabBarController 時會有用處
    open var hidesBottomBarWhenPushed: Bool 
    // 我們通過 navigationController 來 push / pop 操作
    open var navigationController: UINavigationController? { get } 
}

2.3古瓤、導航欄元素:UINavigationItem 分析

我們可以結合著如下圖片來分析了解:

navigation-bar-elements.png
@available(iOS 2.0, *)
open class UINavigationItem : NSObject, NSCoding {
    // 標題止剖,默認 nil腺阳,在 UI層級的最頂層
    // 可以通過:Debug View Hierachy 查看視圖的層級
    open var title: String?
    // 可以自定義標題視圖(比如:放張圖片),只有它在 UI最頂層才有效
    open var titleView: UIView?

    // 返回鍵 (左邊按鈕)
    open var backBarButtonItem: UIBarButtonItem?
    
    // leftBarButtonItems 和 rightBarButtonItems 在以前只是一個單一按鈕穿香,現(xiàn)在管理著一組按鈕亭引;
    // 這兩分別對應著每個數(shù)組中的第一個元素
    @available(iOS 5.0, *)
    // 左按鈕
    open var leftBarButtonItems: [UIBarButtonItem]?

    @available(iOS 5.0, *)
    // 右按鈕
    open var rightBarButtonItems: [UIBarButtonItem]?
}

主要就是以上元素,如果在這里你沒有任何疑惑皮获,那么我是非常疑惑的焙蚓!為啥?你沒發(fā)現(xiàn)有兩個元素都能控制『左側按鈕』么洒宝?分別是:

  • backBarButtonItem主届;
  • leftBarButtonItem;


如果同時設置了這兩會有什么結果待德?

\color{red}{官方?jīng)]有說君丁,我們都是通過實踐得出的真理:}

前提:previousVC 是上一個頁面,nextVC 是下一個頁面将宪,當發(fā)生 push 時绘闷,有如下規(guī)則:

  1. 如果 nextVC 的 leftBarButtonItem != nil,那么將在 navigationBar 的左邊顯示 nextVC 指定的 leftBarButtonItem较坛;
  2. 如果 nextVC 的 leftBarButtonItem == nil印蔗,previousVC 的 backBarButtonItem != nil,那么將在 navigationBar 的左邊顯示 previousVC 指定的 backBarButtonItem丑勤;
  3. 如果兩者都為 nil 則:
  • 3.1. nextVC 的 navigationItem.hidesBackButton = YES华嘹,那么 navigationBar 將隱藏左側按鈕;
  • 3.2. 否則 navigationBar的左邊將顯示系統(tǒng)提供的默認返回按鈕法竞;


我們從以上規(guī)則中發(fā)現(xiàn):

  1. leftBarButtonItem 的優(yōu)先級比 backBarButtonItem 要高耙厚;
  2. backBarButtonItem 是來自上一個頁面,如果當前 VC 是第一個頁面岔霸,那么它沒有上一個頁面薛躬,也就沒有 backBarButtonItem;
  3. leftBarButtonItem 是來自當前頁面呆细,與上個頁面無關型宝,因此,如果當前 VC 是第一個頁面絮爷,那么設置了 leftBarButtonItem 就會很奇怪趴酣;

因此,請注意上面的左側按鈕規(guī)則坑夯,千萬不要同時設置岖寞,雖然有優(yōu)先級保證不會顯示兩個按鈕,但是你的顯示邏輯可能就不一樣了渊涝!

分析完了這么多慎璧,我們用一張大圖來總結一下:

UINavigationController.png

三床嫌、UINavigationController 的使用

3.1跨释、使用方式

它的使用很簡單胸私,逃脫不開的模式,即 window.rootViewController 的設置有以下三種:

  1. 直接設置鳖谈;
// AppDelegate 中
window?.rootViewController = UINavigationController(rootViewController: XXXViewController())
  1. UITabBarController 嵌套 UINavigationController岁疼;
// AppDelegate 中
window?.rootViewController = MainTabBarController()

// MainTabBarController 中
let xxx = UINavigationController(rootViewController: XXXViewController())
xxx.tabBarItem.title = "xxx"
viewControllers = [xxx, ......]
  1. UINavigationController 嵌套 UITabBarController(可能再嵌套 UINavigationController,就和第2種差不多了)缆娃;
// AppDelegate 中
window?.rootViewController = UINavigationController(rootViewController: MainTabBarController())

// 可能再嵌套如下
// MainTabBarController 中
let xxx = UINavigationController(rootViewController: XXXViewController())
xxx.tabBarItem.title = "xxx"
viewControllers = [xxx, ......]

通過以上方式捷绒,我們能發(fā)現(xiàn),常用的模式不是第 1 種贯要,就是第 2 種暖侨。

3.2、實戰(zhàn)出真理

基于我們上一篇的例子崇渗,我們采用第 2 種方式來使用字逗。

  • 簡單改造如下
wrap-with-uinavigation-controller.png
  • 修改 HomeViewController
homevc.png
  • 運行模擬器
run-simu.png

我們的導航欄(帶標題)就出來了,這里需要注意一點:iOS 7.0 后宅广,改為了扁平風格葫掉,這里的導航欄也就變成的半透明效果!

3.3跟狱、頁面跳轉 push & pop

  • 修改我們的 HomeViewController
tap.png
  • 點擊 label俭厚,push 到下一級頁面
push.png
  • 跳轉穩(wěn)定后,如下圖
back.png

四驶臊、總結

本篇只是分析了導航控制器和最基本的使用挪挤,下一篇,我們會根據(jù)實際的場景及需求(上小節(jié)最后一張圖关翎,我們可以看到电禀,push 到下級頁面,底部 tabbar 仍舊顯示笤休,雖然少數(shù) app 會這么做尖飞,但大多數(shù) app 都會隱藏),結合著 UITabBarController 和 UINavigationController 來講述我在實際工作中是如何來配置的店雅。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末政基,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子闹啦,更是在濱河造成了極大的恐慌沮明,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窍奋,死亡現(xiàn)場離奇詭異荐健,居然都是意外死亡酱畅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門江场,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纺酸,“玉大人,你說我怎么就攤上這事址否〔褪撸” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵佑附,是天一觀的道長樊诺。 經(jīng)常有香客問我,道長音同,這世上最難降的妖魔是什么词爬? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮权均,結果婚禮上顿膨,老公的妹妹穿的比我還像新娘。我一直安慰自己螺句,他們只是感情好虽惭,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛇尚,像睡著了一般芽唇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上取劫,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天匆笤,我揣著相機與錄音,去河邊找鬼谱邪。 笑死炮捧,一個胖子當著我的面吹牛,可吹牛的內容都是我干的惦银。 我是一名探鬼主播咆课,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扯俱!你這毒婦竟也來了书蚪?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤迅栅,失蹤者是張志新(化名)和其女友劉穎殊校,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體读存,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡为流,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年呕屎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敬察。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡秀睛,死狀恐怖,靈堂內的尸體忽然破棺而出静汤,到底是詐尸還是另有隱情琅催,我是刑警寧澤居凶,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布虫给,位于F島的核電站,受9級特大地震影響侠碧,放射性物質發(fā)生泄漏抹估。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一弄兜、第九天 我趴在偏房一處隱蔽的房頂上張望药蜻。 院中可真熱鬧,春花似錦替饿、人聲如沸语泽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踱卵。三九已至,卻和暖如春据过,著一層夾襖步出監(jiān)牢的瞬間惋砂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工绳锅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留西饵,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓鳞芙,卻偏偏與公主長得像眷柔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子原朝,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容