前言
最近在開發(fā)過程中遇到了一些問題斧吐,是關(guān)于頁面跳轉(zhuǎn)的屋讶,查了網(wǎng)上的相關(guān)資料冰寻,發(fā)現(xiàn)大家說的不是很完整,決定自己總結(jié)一下皿渗。話不多說斩芭,進(jìn)入正題:頁面間跳轉(zhuǎn)/切換的方式主要有以下四種:
1.模態(tài)彈出。
2.UINavigationController的堆棧式Push和Pop乐疆。
3.改變Window的根視圖划乖。
4.改變window。(沒錯(cuò)挤土,window也是可以改變/更換的)
一琴庵、模態(tài)彈出
首先,關(guān)于模態(tài)彈出頁面的跳轉(zhuǎn)方式仰美,我們必須了解清楚presentingViewController和presentedViewController這兩個(gè)屬性迷殿。樓主英文水平有限,僅憑文檔注釋并不能真正理解咖杂。所以親自做了測(cè)試驗(yàn)證(驗(yàn)證過程很簡(jiǎn)單庆寺,在每一個(gè)跳轉(zhuǎn)的頁面打印self.presentingViewController和self.presentedViewController,在此不多贅述)诉字∨吵ⅲ【驗(yàn)證結(jié)果】:presentsViewController?屬性返回父節(jié)點(diǎn)(上一級(jí)頁面)知纷,presentsViewController?屬性返回子節(jié)點(diǎn)(下一級(jí)頁面),如果沒有父節(jié)點(diǎn)或子節(jié)點(diǎn)导披,返回null屈扎。切記:這兩個(gè)屬性返回的是當(dāng)前節(jié)點(diǎn)(當(dāng)前頁面)直接相鄰父子節(jié)點(diǎn)(上下頁面)埃唯,并不是返回最底層(根視圖頁面)或者最頂層(第一相應(yīng)者頁面)的節(jié)點(diǎn)撩匕。(括號(hào)里的注解是樓主更好理解的說法)
1.最基礎(chǔ)的A彈出B(A->B)
[self presentViewController:B? animated:YES completion:nil];//從當(dāng)前界面A到下一個(gè)頁面B
[self dismissViewControllerAnimated:YES completion:nil];//從B頁面模態(tài)返回。
【注】細(xì)心的程序猿發(fā)現(xiàn):在B頁面用self.presentingViewController 調(diào)用dismissViewControllerAnimated:YES completion:nil 也能實(shí)現(xiàn)從B頁面模態(tài)返回墨叛。那么兩者有沒有區(qū)別呢止毕?答案是:沒有區(qū)別。文檔顯示:父節(jié)點(diǎn)負(fù)責(zé)調(diào)用dismiss來關(guān)閉他彈出來的子節(jié)點(diǎn)漠趁,你也可以直接在子節(jié)點(diǎn)中調(diào)用dismiss方法扁凛,UIKit會(huì)通知父節(jié)點(diǎn)去處理。也就是說:[self dismissViewControllerAnimated:YES completion:nil]的實(shí)質(zhì)是通知其父節(jié)點(diǎn)VC調(diào)用 [self dismissViewControllerAnimated:YES completion:nil]闯传;而 [self .presentingViewController dismissViewControllerAnimated:YES completion:nil] 就是直接讓其父節(jié)點(diǎn)VC執(zhí)行撤銷操作谨朝。(有點(diǎn)繞,但是我相信你看的懂)
2.A彈出B甥绿,B再彈出C(A->B->C)
從當(dāng)前界面A到頁面B和從B界面到頁面C代碼是一樣的字币,同樣,從C頁面模態(tài)返回和從B頁面模態(tài)返回代碼也是一樣的共缕。(廢話洗出,但還是要說一下)
如何直接從C返回到A呢?
A彈出B之后图谷,可不可以再用A彈出C呢翩活?
【警告內(nèi)容】:“嘗試在A上彈C,但是A已經(jīng)彈了B”便贵。也就是說彈模態(tài)視圖的時(shí)候菠镇,只能用最頂層的的控制器去彈,用底層的控制器去彈會(huì)失敗承璃,并拋出警告利耍。
關(guān)于獲取viewController的最頂層子節(jié)點(diǎn)和最底層父節(jié)點(diǎn),大家可以參考下方的這個(gè)绸硕,寫到我們程序的工具類方法里堂竟。
【常見錯(cuò)誤】:在viewDidLoad或者viewWillAppear方法中模態(tài)頁面。
上述代碼都會(huì)失敗玻佩,first VC也不會(huì)彈出出嘹,并會(huì)拋出上面的警告。因?yàn)閟elf.view還沒有被添加到視圖樹(父視圖)咬崔,不允許彈出視圖税稼。也就是說烦秩,如果一個(gè)的viewController的視圖還沒被添加到視圖樹(父視圖)上,那么不可以用這個(gè)viewController再區(qū)彈出其他頁面郎仆。
二只祠、UINavigationController的堆棧式Push和Pop
UINavigationController的Push/Pop頁面和《數(shù)據(jù)結(jié)構(gòu)》中的堆棧方式很像,遵守棧的特點(diǎn):先進(jìn)后出扰肌,后進(jìn)先出抛寝。【想象一下】一個(gè)狹長的電梯曙旭,先進(jìn)入的人在里面盗舰,后進(jìn)入的靠近門口,出電梯時(shí)桂躏,靠近門口的(也就是后進(jìn)入電梯的)先出去钻趋,電梯里面的(先進(jìn)入電梯的)后出去。
需要注意的是push和pop是UINavigationController和其子類才有的方法剂习,普通的控制器是沒有的蛮位。 所以,所有的Push和Pop操作(出棧/入棧 )都是依賴于self.navigationController 調(diào)用的鳞绕。
1.最基礎(chǔ)的A Push出B(A->B)
首先失仁,這里實(shí)際指的是NavigationController依次push出AB兩個(gè)頁面。
[self.navigationController pushViewController:B animated:nil];//從當(dāng)前界面A到下一個(gè)頁面B(入棧)
[self.navigationController popViewControllerAnimated:YES];//從B頁面pop返回猾昆。
那么可不可以用self.presentingViewController 來表示要返回的頁面呢陶因。于是猜想并嘗試如下返回方式:
由于UINavigationController是一個(gè)視圖控制器的容器,他里面可能放了很多個(gè)控制器垂蜗,而每個(gè)控制器之前并不是父/子節(jié)點(diǎn)的關(guān)系楷扬。所以,[self.navigationController popToViewController:self.presentingViewController animated:YES];的返回方式是錯(cuò)誤的
【驗(yàn)證】:1.在一個(gè)普通的頁面P模態(tài)彈出一個(gè)帶有NavigationController的頁面贴见。2.在一個(gè)帶有NavigationController的頁面模態(tài)一個(gè)普通的頁面P烘苹。
通過打印self.presentingViewController和self.presentedViewController可以發(fā)現(xiàn):頁面P的控制器和UINavigationController是屬同一級(jí)的,其他的均顯示null片部。也就是說我們可以通過判斷self.presentingViewController是否為null來確定當(dāng)前頁面創(chuàng)建之前有沒有進(jìn)行過模態(tài)彈出操作镣衡。
延伸一個(gè)判斷當(dāng)前返回方式的方法:
2.A push出B,B push出C?
同樣档悠,這里實(shí)際指的是NavigationController依次push出ABC三個(gè)頁面廊鸥。那么想直接從C返回到A怎么實(shí)現(xiàn)呢?
同樣辖所,不可以使用[self.navigationController popToViewController:self.presentingViewController.presentingViewController animated:YES];的方式惰说。
3.返回到根頁面
[self.navigationController popToRootViewController];
4.返回到指定某個(gè)頁面
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:2] animated:YES];當(dāng)然,也可以使用上面??第二條的方法缘回。
三吆视、改變Window的根視圖
改變window根視圖的方法很簡(jiǎn)單典挑,創(chuàng)建一個(gè)要跳轉(zhuǎn)的頁面或NavigationController,然后用setRootViewController方法設(shè)置即可啦吧。
四您觉、改變Window
在對(duì)接一些已經(jīng)封裝好(封裝度較高)的第三方SDK時(shí)發(fā)現(xiàn):自己程序里已有的NavigationController樣式顏色或者TabBarController樣式與之沖突,而且修改起來相當(dāng)麻煩授滓,這個(gè)時(shí)候建議你直接更換一個(gè)Window琳水,然后所有問題完美解決。(如果你真的碰到過這種情況褒墨,你一定會(huì)感謝我的)
1.更換新的window
2.從新的window頁面返回原來的window
【特別用法】:如果你想實(shí)現(xiàn)S->A->B->C-...>N,并且在C...N之間的某頁面可以直接回到B炫刷。可以在B頁面模態(tài)一個(gè)帶有NavigationController的頁面郁妈,之后C...N間的頁面跳轉(zhuǎn)用NavigationController的方法,當(dāng)需要直接返回B時(shí)绍申,可以直接調(diào)用模態(tài)返回方法:[self dismissViewControllerAnimated:YES completion:nil];實(shí)現(xiàn)噩咪。(是不是很神奇?????)希望對(duì)你有所幫助极阅。