present/push的恩怨情仇

  • push/pop
    • 简介
    • VC栈存储
      • container view controller
      • 优势
      • dealloc
    • navigationController
      • 无navigationController的界面push
      • 添加navigationController使其可以push
        • 缺点
      • self.navigationController的dismiss
        • 1. 前面有从`present`到的界面
          • 一个神奇的事情
        • 2. 前面没有`present`到的界面
  • present/dismiss
    • 简介
    • presentingViewController和presentedViewController
      • 实验
      • 总结
    • 返回根视图---1
      • dismiss
      • 利用dismiss
      • 但是
    • 返回根视图---2
    • 切换根视图的小问题

push/pop

简介

pushViewController 导航控制器入栈的方式切换页面

popViewController
导航控制器出栈的方式返回上一页面

VC栈存储

container view controller

container view controller 指的是VC的容器类,通过container view controller,我们可以很方便的管理子VC,实现VC之间的跳转等,iOS中container view controller包括 UINavigationController, UISplitViewController, 以及 UIPageViewController.

push/pop是将旧视图存储在了VC栈中,因此我们可以这样操作:

假如

从A—(push)—>B—(push)—>C—(push)—>D—(popToRootViewController)—>A

如果在D视图进行操作:

NSLog(@"%@",self.navigationController.viewControllers);

则会打印:

2019-09-28 19:42:50.576198+0800 1111[995:32530] ("<AViewController: 0x7fdc41d00480>","<BViewController: 0x7fdc3ec0a010>","<CViewController: 0x7fdc3ef1b1e0>","<DViewController: 0x7fdc3ef1b530>"
)

这就是push的VC栈的内容。
因此,我们可以进行这样的pop跳转:

    [self.navigationController popToViewController:self.navigationController.viewControllers[0] animated:YES];

这是因为我们可以看到,viewControllersNSArray类型的,所以可以通过下标跳转到指定界面。

优势

VC栈存储的优势在于:

  1. 可以直接跳转到根视图:

    • 通过popToRootViewController方法,把栈清空,直接回到根视图。
  2. 可以跳转到指定界面:

    • 通过popToViewController:方法 和 NSArray类型的viewControllers,可以跳转到指定的视图。

dealloc

那么,在我们通过popToRootViewController方法回到根视图的时候,A视图后面的B、C、D视图会怎样呢?

答案是:按数组顺序依次销毁。
可以通过在B、C、D视图添加:

- (void)dealloc {NSLog(@"当前视图名称 dealloc");
}

可以发现,最后会这样打印:

2019-09-28 20:00:39.291619+0800 1111[1108:40814] AViewController viewWillAppear
2019-09-28 20:00:39.296359+0800 1111[1108:40814] BViewController dealloc
2019-09-28 20:00:39.296798+0800 1111[1108:40814] CViewController dealloc
2019-09-28 20:00:39.802419+0800 1111[1108:40814] DViewController dealloc

会先出现A视图,然后按数组顺序依次dealloc。

也就是说,它是按栈存储的,但是并不符合后进先出的原理。(???)

可能是把栈里的视图先取出,放到里另一个栈sNew里,当top指的是根视图时,先把根视图显示,再把栈sNew里的视图依次移除

navigationController

讨论push/pop,就不得不讨论navigationController

无navigationController的界面push

当我们从一个有navigationController的A界面用present跳转到B界面后,B界面将没有navigationController,若还在B界面用push方法,即如下:

    CViewController *cViewController = [[CViewController alloc]init];NSLog(@"跳转到C界面");[self.navigationController pushViewController:cViewController animated:YES];

则界面不会跳转,会一直停留在B界面,并且会触发C界面的dealloc方法(不会触发loadView方法):

2019-09-28 20:29:08.648083+0800 1111[1270:53904] 跳转到C界面
2019-09-28 20:29:08.648264+0800 1111[1270:53904] CViewController dealloc

添加navigationController使其可以push

假如我们想要达到这样的效果:

A—(present)—>B—(present)—>C—(push)—>D

B界面到C界面用present方法到原因前面已经讲过里,那么我们如何达到由present到的可以使用push方法呢?

这时就可以给要present到界面加个navigationController了:

    CViewController *cViewController = [[CViewController alloc]init];UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:cViewController];[self presentViewController:nav animated:YES completion:nil];

这时,在C界面,就可以使用push方法到达D界面了。

缺点

但是,这样到操作有个缺点:会把VC栈更新,也就是说,我们找不会之前的根视图了,新的根视图会变成C,在D界面进行打印,结果如下:

2019-09-28 21:00:16.317749+0800 1111[1503:67795] ("<CViewController: 0x7fd9d9513bd0>","<DViewController: 0x7fd9d9510af0>"
)

self.navigationController的dismiss

看到这个是不是感觉很神奇?

不是只有self才能调用dismiss方法,self.navigationController也可以调用这个方法,那这个方法有什么效果呢?

(其实直接self调用得到的结果也一样)

1. 前面有从present到的界面

若是前面有从present到的界面,那么会回到第一个present到的界面

例如:

A—(present)—>B—(present)—>C—(push)—>D

在D界面进行:

[self.navigationController dismissViewControllerAnimated:YES completion:nil];

则会直接回到B界面,且C、D界面将会销毁:

2019-09-28 21:32:56.522269+0800 1111[1822:83303] CViewController dealloc
2019-09-28 21:32:56.522559+0800 1111[1822:83303] DViewController dealloc
一个神奇的事情

发现dismiss到B界面时,C、D界面的销毁顺序不定:

2019-09-28 21:56:16.228338+0800 1111[2062:94506] DViewController dealloc
2019-09-28 21:56:16.228534+0800 1111[2062:94506] CViewController dealloc

这…恕我能力有限,不知道为什么…

2. 前面没有present到的界面

会停留在本界面

例如:

A—(push)—>B—(push)—>C—(push)—>D

在D界面进行:

[self.navigationController dismissViewControllerAnimated:YES completion:nil];

则会停留在D界面,不发生跳转。

present/dismiss

简介

presentViewController / dismissViewController

模态切换的方式切换页面
(压入一个新视图和弹出顶层视图)
(要逐级跳转)

presentViewController 弹出,出现一个新视图

presentingViewController和presentedViewController

既然讲到present,就不得不讲一下presentingViewControllerpresentedViewController了。

实验

例如:

A—(present)—>B

那么对于A来说:

2019-09-29 01:02:26.510791+0800 1111[4015:194177] AViewController presented--------<BViewController: 0x7f889e507e70>
2019-09-29 01:02:26.510873+0800 1111[4015:194177] AViewController presenting--------(null)

那么对于B来说:

2019-09-29 01:03:28.802197+0800 1111[4035:196590] BViewController presented--------(null)
2019-09-29 01:03:28.802321+0800 1111[4035:196590] BViewController presenting--------<AViewController: 0x7ff2c58062b0>

总结

presented代表到是当前界面的子视图

presenting代表到是当前界面的父视图

返回根视图—1

在知道push可以利用栈存储的方法,轻松跳转回根视图后,不妨想想present可不可以直接跳回根视图?又是怎么跳转的?

dismiss

要弄清楚如何跳转到根视图,我们可以先弄清楚dismiss的原理。

  1. 在某一视图中dismiss ,它会找从当前视图 present 的视图,这时候会有2种可能:

    • 当前视图没有present的视图,这时候,它就会返回到presenting视图(父视图),触发父视图的dismiss,把自己dealloc
    • 当前视图有present的视图,这时候,它会停留在当前视图,并把prsented视图(子视图)全部dealloc

例如:

A—(present)—>B—(present)—>C—(present)—>D

如果在A界面进行dismiss操作,则会跳回A界面,并把B、C、D界面dealloc,结果如下:

2019-09-29 00:07:41.900487+0800 1111[3562:152312] DViewController dealloc
2019-09-29 00:07:41.900944+0800 1111[3562:152312] CViewController dealloc
2019-09-29 00:07:41.901692+0800 1111[3562:152312] AViewController viewWillAppear
2019-09-29 00:07:42.405926+0800 1111[3562:152312] BViewController dealloc

利用dismiss

这样,我们就可以利用dissmiss这个原理,轻松的从D界面回到A界面。

也就是说,只要我们能在A界面调用到dismiss方法,就可以返回A界面了。

那么,我们如何在D界面点击按钮到时候,触发A界面到dismiss方法呢?

这时候,我们会想到传值,而在那么多多传值方法里,能最简单让多个界面响应的,当然是通知传值啦。

因此,我们在D界面写:

[[NSNotificationCenter  defaultCenter]postNotificationName:@"back to A" object:nil];

在A界面的viewDidLoad里面写:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backA) name:@"back to A" object:nil];

并在A界面添加backA方法:

-(void)backA {[self dismissViewControllerAnimated:YES completion:nil];
}

这样,就可以实现回到根视图的要求了。

但是

但是,这样的返回根视图,并不是直接到达的根视图,而是会经过中间的所有界面。

还是这个例子:

A—(present)—>B—(present)—>C—(present)—>D

如果在B、C、D界面重写viewWillAppeardealloc方法:

- (void)dealloc {NSLog(@"当前视图名称 dealloc");
}- (void)viewWillAppear:(BOOL)animated {NSLog(@"当前视图名称 viewWillAppear");
}

进行A界面的dismiss操作,这时候,会发现打印情况如下:

2019-09-29 00:07:41.896396+0800 1111[3562:152312] CViewController viewWillAppear
2019-09-29 00:07:41.898284+0800 1111[3562:152312] BViewController viewWillAppear
2019-09-29 00:07:41.900487+0800 1111[3562:152312] DViewController dealloc
2019-09-29 00:07:41.900944+0800 1111[3562:152312] CViewController dealloc
2019-09-29 00:07:41.901692+0800 1111[3562:152312] AViewController viewWillAppear
2019-09-29 00:07:42.405926+0800 1111[3562:152312] BViewController dealloc

通过打印情况,我们可以发现,在回到A界面的过程中,其实是经过了B、C界面的。

返回根视图—2

在上面,利用了通知 加 dismiss的方法达到了返回根视图的目的(其实也可以返回指定视图),其实还有一种方法。

新写一个继承于UIViewControllerBase_VC,并且写上一个方法:

-(void)toRootViewController{UIViewController *viewController = self;while (viewController.presentingViewController) {//判断是否为最底层控制器if ([viewController isKindOfClass:[Base_VC class]]) {viewController = viewController.presentingViewController;}else{break;}}if (viewController) {[viewController dismissViewControllerAnimated:YES completion:nil];}
}

再以此Base_VC继承,创建A、B、C、D界面,这样就会逐级判断,直到到达根视图,执行dismiss操作。

详细操作可以看这篇博客(有源码):
多级presentViewController直接返回一级界面

切换根视图的小问题

切换根视图

还是这个例子:

A—(present)—>B—(present)—>C—(present)—>D

若是在C界面跳转到D界面的时候,并不是直接跳转,而是把D界面设为了根视图,那么A、B、C界面会怎样呢?

根据实验,我们得知,把D设为了根视图,但是A、B、C界面依旧存在,当D界面较小时,我们可以看到底下的A、B、C界面。

在A、B、C界面重写dealloc方法:

- (void)dealloc {NSLog(@"当前视图名称 dealloc");
}

查看打印情况:

2019-09-29 13:01:50.277999+0800 1111[1759:50148] AViewController viewWillAppear
2019-09-29 13:01:52.786515+0800 1111[1759:50148] BViewController viewWillAppear
2019-09-29 13:01:53.575938+0800 1111[1759:50148] CViewController viewWillAppear
2019-09-29 13:01:54.376980+0800 1111[1759:50148] DViewController viewWillAppear

发现,A、B、C界面并没有执行dealloc方法,即没有销毁视图,那么我们应该如何使其销毁呢?

我们会想到在A界面dismiss的办法,这样的打印结果又是怎样的呢?

2019-09-29 13:07:51.106658+0800 1111[1825:53044] BViewController viewWillAppear
2019-09-29 13:07:51.107801+0800 1111[1825:53044] CViewController dealloc
2019-09-29 13:07:51.109261+0800 1111[1825:53044] AViewController viewWillAppear
2019-09-29 13:07:51.612881+0800 1111[1825:53044] BViewController dealloc
2019-09-29 13:07:51.613156+0800 1111[1825:53044] AViewController dealloc

可以看到,A、B、C界面都执行了dealloc方法,打算但是我们看手机,会发现,虽然A界面销毁了,可是依旧在D界面的下面显示着。

???

这就很神奇了,通过百度查找(iOS连续dismiss几个ViewController的方法,以及切换根视图我遇到的问题 - 简书)

发现了一种方法:

在C界面跳转到D界面处写:

    [[NSNotificationCenter  defaultCenter]postNotificationName:@"back to A" object:nil];

在A界面的viewDidLoad写:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backA) name:@"back to A" object:nil];

最后,在通知的方法里写:

-(void)backA {[self dismissViewControllerAnimated:YES completion:^{DViewController *dViewController = [[DViewController alloc]init];[UIApplication sharedApplication].delegate.window.rootViewController = dViewController;}];
}

最后手机屏幕上只剩下D界面,且打印结果如下:

2019-09-29 13:00:09.846634+0800 1111[1726:48963] BViewController viewWillAppear
2019-09-29 13:00:09.848979+0800 1111[1726:48963] CViewController dealloc
2019-09-29 13:00:09.849789+0800 1111[1726:48963] AViewController viewWillAppear
2019-09-29 13:00:10.354176+0800 1111[1726:48963] DViewController viewWillAppear
2019-09-29 13:00:10.355783+0800 1111[1726:48963] BViewController dealloc
2019-09-29 13:00:10.357705+0800 1111[1726:48963] AViewController dealloc

也就是说,要在dismiss执行后,再切换根视图,就可以成功的将之前的界面dealloc掉,并且屏幕上只留新的根视图。

present/push的恩怨情仇相关推荐

  1. 漫画:前端发展史的江湖恩怨情仇

    作者 | 前端布道师 来源 | 前端布道师(ID:honeyBadger8) 时间总是过得很快, 似乎快得让人忘记了昨天,前端WEB领域的发展更是如此,转眼间已是近30年,时光荏苒,初心不变,在一代又 ...

  2. 宏观与量子的恩怨情仇

    第四章:宏观与量子的恩怨情仇 我们知道哥本哈根诠释由波尔和海森堡于1927年在哥本哈根合作研究时共同提出的.此诠释建立在由德国数学家.物理学家Max Born所提出的"波函数的概率表达&qu ...

  3. [你必须知道的.NET]第一回:恩怨情仇:is和as

    本文将介绍以下内 容: • 类型转换 • is/as操作符小议 1. 引言 类型安全是.NET设计之初重点考虑 的内容之一,对于程序设计者来说,完全把握系统数据的类型安全,经常是力不从心的问题.现在, ...

  4. [你必须知道的.NET] 第一回:恩怨情仇:is和as

    发布日期:2007.4.7 作者:Anytao ©2007 Anytao.com 转贴请注明出处,留此信息. 本文将介绍以下内容: • 类型转换 • is/as操作符小议 1. 引言 类型安全是.NE ...

  5. 红帽 与 CentOS 之间的恩怨情仇

    [CSDN 编者按]红帽正式宣布 CentOS 8 于 2021年底结束支持,后续将由 CentOS Stream 接班.一起来看看红帽与 CentOS 的"恩怨情仇"-- 参考链 ...

  6. Usdt到底靠谱吗?——记美国与大B网的恩怨情仇

    Usdt到底靠谱吗?--记美国与大B网的恩怨情仇 在介绍之前,首先为小白科普几个Usdt的问题,方便大家阅读. Usdt是什么? Usdt是Tether公司的发行的一个基于区块链技术的代币,发行之时对 ...

  7. 钟汉良日记:网络也是江湖,有恩怨情仇有利益纠葛

    2022年10月5日 周三 天气多云 这几天我写的日记,少了一个平台,猜一猜是哪个平台?其实,就是知乎这个问答平台,谈到乐买买,被关小黑屋了. 我的日记在知乎上更新了三十多篇了,获得的阅读量少的可怜, ...

  8. 乔布斯与比尔盖茨的传奇人生 两位天才的恩怨情仇

    摘要:苹果公司联合创始人乔布斯的去世,让世人惋惜,这位天才的影响力远远超出PC行业,他改变了人们的生活方式,也影响着我们对生活的态度.作为美国两大影响世界的人物,苹果创始人乔布斯与比尔盖茨经常被人拿出 ...

  9. 细数研究生和导师的那些恩怨情仇

    阅读本文大概需要 5 分钟. 作者:黄小斜 这篇文章其实我很早之前就想写了,没想到最近又出了一件类似的事情,事情就发生在我刚毕业不久的学校,事情始末想必大家都已经看过,震惊和惋惜之余,更多的是思考. ...

最新文章

  1. colab找不到模块 no name
  2. Iptables防火墙配置详解
  3. java扫描注解_使用Spring Java注释扫描
  4. 在python中等号前面与后面分别是什么意思-Python中冒号等于(:=)是什么意思?...
  5. bat窗口大小设置_Tomcat的JVM和连接数设置
  6. qt乱码Could not decode“xxx.cpp“ with “UTF-8“-encoding.Editing not possible问题处理
  7. [BUGKU][CTF][Reverse][2020] Reverse writeup 1-7 暂时肝不动了
  8. 钉钉功能介绍_平棉集团组织召开阿里钉钉办公系统基础功能培训会
  9. Zernike函数拟合曲面--MATLAB实现
  10. 【Java】Springboot项目中Transactional的使用方式
  11. 2019年,区块链不得不知的 9 件大事!
  12. linux fcntl函数,Linux C 学习之 - fcntl 函数
  13. 软件开发人员的简历项目经验怎么写?
  14. SpringBoot项目下载resources目录下模板文件
  15. OpenCV: 读取图片中某个点的像素值
  16. Manjaro 安装搜狗输入法
  17. 中国象棋马走日(要求打印每一种走法) — 递归
  18. element-ui input 身份证号码验证
  19. 推荐!十个好用的百度网盘搜索引擎
  20. 车羊问题c语言编程,再谈“羊车门”问题

热门文章

  1. PHP curl请求处理
  2. 微信代驾小程序-景德镇代驾服务平台小程序源码分享
  3. 从单张图重建三维人体模型综述(四)
  4. 京东热key探测框架本地压测数据记录,单机(8核)QPS约16万/s,可水平扩展
  5. 浅谈ENVI应用前景及未来发展方向
  6. 解决 Error type 3 问题
  7. DeepMind:星际争霸2:强化学习新挑战(论文翻译)
  8. Scratch少儿编程案例-坦克大战-双人对战
  9. 阿里云官网www.aliyun.com
  10. 动手实现简易网站目录扫描器——WebScanner