iOS-UIViewController頁面間相互跳轉(zhuǎn)總結(jié)

前言

最近在開發(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è)屬性。樓主英文水平有限,僅憑文檔注釋并不能真正理解。所以親自做了測試驗(yàn)證(驗(yàn)證過程很簡單,在每一個(gè)跳轉(zhuǎn)的頁面打印self.presentingViewController和self.presentedViewController,在此不多贅述)?!掘?yàn)證結(jié)果】:presentsViewController?屬性返回父節(jié)點(diǎn)(上一級頁面),presentsViewController?屬性返回子節(jié)點(diǎn)(下一級頁面),如果沒有父節(jié)點(diǎn)或子節(jié)點(diǎn),返回null。切記:這兩個(gè)屬性返回的是當(dāng)前節(jié)點(diǎn)(當(dāng)前頁面)直接相鄰父子節(jié)點(diǎn)(上下頁面),并不是返回最底層(根視圖頁面)或者最頂層(第一相應(yīng)者頁面)的節(jié)點(diǎn)。(括號里的注解是樓主更好理解的說法)

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呢?

C返回到A

A彈出B之后,可不可以再用A彈出C呢?


A直接彈出C

【警告內(nèi)容】:“嘗試在A上彈C,但是A已經(jīng)彈了B”。也就是說彈模態(tài)視圖的時(shí)候,只能用最頂層的的控制器去彈,用底層的控制器去彈會(huì)失敗,并拋出警告。

關(guān)于獲取viewController的最頂層子節(jié)點(diǎn)和最底層父節(jié)點(diǎn),大家可以參考下方的這個(gè),寫到我們程序的工具類方法里。

最頂層子節(jié)點(diǎn)和最底層父節(jié)點(diǎn)

【常見錯(cuò)誤】:在viewDidLoad或者viewWillAppear方法中模態(tài)頁面。

錯(cuò)誤的使用位置
打印警告

上述代碼都會(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 來表示要返回的頁面呢。于是猜想并嘗試如下返回方式:

天真的猜想
猜想結(jié)果

由于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。

打印父/子節(jié)點(diǎn)

通過打印self.presentingViewController和self.presentedViewController可以發(fā)現(xiàn):頁面P的控制器和UINavigationController是屬同一級的,其他的均顯示null。也就是說我們可以通過判斷self.presentingViewController是否為null來確定當(dāng)前頁面創(chuàng)建之前有沒有進(jìn)行過模態(tài)彈出操作。

延伸一個(gè)判斷當(dāng)前返回方式的方法:

判斷當(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根視圖的方法很簡單,創(chuàng)建一個(gè)要跳轉(zhuǎn)的頁面或NavigationController,然后用setRootViewController方法設(shè)置即可。

更換window的根視圖

四、改變Window

在對接一些已經(jīng)封裝好(封裝度較高)的第三方SDK時(shí)發(fā)現(xiàn):自己程序里已有的NavigationController樣式顏色或者TabBarController樣式與之沖突,而且修改起來相當(dāng)麻煩,這個(gè)時(shí)候建議你直接更換一個(gè)Window,然后所有問題完美解決。(如果你真的碰到過這種情況,你一定會(huì)感謝我的)

1.更換新的window

更換新的window

2.從新的window頁面返回原來的window

返回原來的window

【特別用法】:如果你想實(shí)現(xiàn)S->A->B->C-...>N,并且在C...N之間的某頁面可以直接回到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)。(是不是很神奇?????)希望對你有所幫助。

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

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

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