iOS-UIViewController页面间相互跳转总结

前言

最近在开发过程中遇到了一些问题,是关于页面跳转的,查了网上的相关资料,发现大家说的不是很完整,决定自己总结一下。话不多说,进入正题:页面间跳转/切换的方式主要有以下四种:

1.模态弹出。

2.UINavigationController的堆栈式Push和Pop。

3.改变Window的根视图。

4.改变window。(没错,window也是可以改变/更换的)

一、模态弹出

首先,关于模态弹出页面的跳转方式,我们必须了解清楚presentingViewController和presentedViewController这两个属性。楼主英文水平有限,仅凭文档注释并不能真正理解。所以亲自做了测试验证(验证过程很简单,在每一个跳转的页面打印self.presentingViewController和self.presentedViewController,在此不多赘述)。【验证结果】:presentsViewController 属性返回父节点(上一级页面),presentsViewController 属性返回子节点(下一级页面),如果没有父节点或子节点,返回null。切记:这两个属性返回的是当前节点(当前页面)直接相邻父子节点(上下页面),并不是返回最底层(根视图页面)或者最顶层(第一相应者页面)的节点。(括号里的注解是楼主更好理解的说法)

1.最基础的A弹出B(A->B)

[self presentViewController:B  animated:YES completion:nil];//从当前界面A到下一个页面B

[self dismissViewControllerAnimated:YES completion:nil];//从B页面模态返回。

【注】细心的程序猿发现:在B页面用self.presentingViewController 调用dismissViewControllerAnimated:YES completion:nil 也能实现从B页面模态返回。那么两者有没有区别呢?答案是:没有区别。文档显示:父节点负责调用dismiss来关闭他弹出来的子节点,你也可以直接在子节点中调用dismiss方法,UIKit会通知父节点去处理。也就是说:[self dismissViewControllerAnimated:YES completion:nil]的实质是通知其父节点VC调用 [self dismissViewControllerAnimated:YES completion:nil];而 [self .presentingViewController dismissViewControllerAnimated:YES completion:nil] 就是直接让其父节点VC执行撤销操作。(有点绕,但是我相信你看的懂)

2.A弹出B,B再弹出C(A->B->C)

从当前界面A到页面B和从B界面到页面C代码是一样的,同样,从C页面模态返回和从B页面模态返回代码也是一样的。(废话,但还是要说一下)

如何直接从C返回到A呢?

C返回到A

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


A直接弹出C

【警告内容】:“尝试在A上弹C,但是A已经弹了B”。也就是说弹模态视图的时候,只能用最顶层的的控制器去弹,用底层的控制器去弹会失败,并抛出警告。

关于获取viewController的最顶层子节点和最底层父节点,大家可以参考下方的这个,写到我们程序的工具类方法里。

最顶层子节点和最底层父节点

【常见错误】:在viewDidLoad或者viewWillAppear方法中模态页面。

错误的使用位置
打印警告

上述代码都会失败,first VC也不会弹出,并会抛出上面的警告。因为self.view还没有被添加到视图树(父视图),不允许弹出视图。也就是说,如果一个的viewController的视图还没被添加到视图树(父视图)上,那么不可以用这个viewController再区弹出其他页面。

二、UINavigationController的堆栈式Push和Pop

UINavigationController的Push/Pop页面和《数据结构》中的堆栈方式很像,遵守栈的特点:先进后出,后进先出。【想象一下】一个狭长的电梯,先进入的人在里面,后进入的靠近门口,出电梯时,靠近门口的(也就是后进入电梯的)先出去,电梯里面的(先进入电梯的)后出去。

需要注意的是push和pop是UINavigationController和其子类才有的方法,普通的控制器是没有的。 所以,所有的Push和Pop操作(出栈/入栈 )都是依赖于self.navigationController 调用的。

1.最基础的A Push出B(A->B)

首先,这里实际指的是NavigationController依次push出AB两个页面。

[self.navigationController pushViewController:B animated:nil];//从当前界面A到下一个页面B(入栈)

[self.navigationController popViewControllerAnimated:YES];//从B页面pop返回。

那么可不可以用self.presentingViewController 来表示要返回的页面呢。于是猜想并尝试如下返回方式:

天真的猜想
猜想结果

由于UINavigationController是一个视图控制器的容器,他里面可能放了很多个控制器,而每个控制器之前并不是父/子节点的关系。所以,[self.navigationController popToViewController:self.presentingViewController animated:YES];的返回方式是错误的

【验证】:1.在一个普通的页面P模态弹出一个带有NavigationController的页面。2.在一个带有NavigationController的页面模态一个普通的页面P。

打印父/子节点

通过打印self.presentingViewController和self.presentedViewController可以发现:页面P的控制器和UINavigationController是属同一级的,其他的均显示null。也就是说我们可以通过判断self.presentingViewController是否为null来确定当前页面创建之前有没有进行过模态弹出操作。

延伸一个判断当前返回方式的方法:

判断当前页面的放回方式

2.A push出B,B push出C 

同样,这里实际指的是NavigationController依次push出ABC三个页面。那么想直接从C返回到A怎么实现呢?

返回到指定页面

同样,不可以使用[self.navigationController popToViewController:self.presentingViewController.presentingViewController animated:YES];的方式。

3.返回到根页面

[self.navigationController popToRootViewController];

4.返回到指定某个页面

[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:2] animated:YES];当然,也可以使用上面👆第二条的方法。

三、改变Window的根视图

改变window根视图的方法很简单,创建一个要跳转的页面或NavigationController,然后用setRootViewController方法设置即可。

更换window的根视图

四、改变Window

在对接一些已经封装好(封装度较高)的第三方SDK时发现:自己程序里已有的NavigationController样式颜色或者TabBarController样式与之冲突,而且修改起来相当麻烦,这个时候建议你直接更换一个Window,然后所有问题完美解决。(如果你真的碰到过这种情况,你一定会感谢我的)

1.更换新的window

更换新的window

2.从新的window页面返回原来的window

返回原来的window

【特别用法】:如果你想实现S->A->B->C-...>N,并且在C...N之间的某页面可以直接回到B。可以在B页面模态一个带有NavigationController的页面,之后C...N间的页面跳转用NavigationController的方法,当需要直接返回B时,可以直接调用模态返回方法:[self dismissViewControllerAnimated:YES completion:nil];实现。(是不是很神奇?😂😂)希望对你有所帮助。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容