你真的了解iOS中控制器的present和dismiss吗?

一、了解present和dismiss

一个iOS开发,这个控制器的打开和关闭,应该是接触UIKit所接触的第一个关于UIViewController的API,然而,你真的了解它吗?
同样的,本文默认你已经了解UIViewController的presentdismiss方法,并多次运用。另外本文不再加OC语言的代码,毕竟都能读得懂swift。

二、谁来调用dismiss方法?

咱们从最基本的开始:一个控制器A,和一个控制器B。


A present B

让A打开B,在A中是这样写:

//A
let B = ViewController()
self.present(B, animated: true, completion: nil)

在B中需要关闭B的时候在B中这么写:

//B
self.dismiss(animated: true, completion: nil)

难道?这样写有问题?这....可能大多数开发者都是这么写,也没有出现过什么问题。但是...
YES,这样写在“理论”讲,是错误的。
别急,在实际上,这么写大多数情况是没有问题的,因为iOS系统帮我们做了很多。

三、presentingViewController和presentedViewController

想了解为什么说那么写在“理论上”是错误的,先从presentingViewControllerpresentedViewController说起。

为了不使本文枯燥,不引用大段描述和说明,只针对案例解释。当从A中弹出B后:

  1. self.presentingViewController: 在A中,就是nil;在B中,就是A
  2. self.presentedViewController:在A中,就是B;在B中,就是nil

那为什么说在B中调用dismiss在“理论上”是错的呢?因为有这么一个规则一定要记住!

谁污染,谁治理!

很熟悉对不对?MRC的法则,谁创建谁释放!所以A打开了B,当然是A来负责关闭!

正确写法是在A里调用self.dismiss(animated: true, completion: nil)

等等,之前都是B里调用?没错,dismiss方法系统会自动优化,当B视图控制器调用dismiss时,它并没有打开任何界面,就将dismissViewController方法会自动交给B的presentingViewController执行,也就是A来执行。
如果现在A没有打开B的话,调用dismissViewController时它没有presentedViewController,转交给它的presentingViewController,但是它也没有presentingViewController,所以dismiss就不执行了。swift中可选型能很好的解释了:

self.presentingViewController?.dismiss(animated: true, completion: nil) //presentingViewController为nil,后面的不执行

扩展:关于实例为空的时候方法不执行,可百度“iOS消息转发”了解。

既然这样,说这个有毛用?
如果你从A打开了B,从B打开了C,现在怎么直接回到A?
你很可能会通过一些手段让B执行dismiss方法,但你会得到错误的结果,因为你这里如果交给B执行dismiss方法,和直接在C里面执行dismiss方法的效果是一样的,也就是说你到了B的界面并没有到A。
SO,dismiss方法必须让需要回到的这个控制器来执行。那么一个控制器的presentingViewController一定是打开它的那个控制器吗?继续往后看。

四、presentingViewController是打开它的那个控制器吗?

从上文,A打开了B,A是B的presentingViewController,那是不是所有的控制器的presentingViewController都是调用presnet方法打开自己的那个控制器呢?
NO!但是刚才A打开了B,不是说A是B的presentingViewController吗?当然,不是说一个控制器A弹出一个控制器B,A就一定是B的presentingViewController。

吃屁吧你

别着急,看几个图片娱乐一下:


第一种

这时箭头指向的控制器的presenting是谁?


第二种

这时箭头指向的控制器的presenting是谁?

第一个图片是navigationController,第二个是tabbarController。又跟你想的不一样了?看来你真的了解的太少,这时咱们得了解一下子控制器了。

四、子控制器childViewControllers

刚才在了解presentingViewController和presentedViewController的时候,如果你用代码试了一下,会发现parentViewController(OC中,swift叫parent),父控制器。有父就有子,每个控制器都有一个属性,叫做childViewControllers,存放它的子控制器。这里对childViewControllers的使用方法使用场景使用优势不做探讨。我们假设你了解并多次使用了子控制器来显示复杂界面的。

但是实际上只要你学习iOS的UIKit,你就肯定已经多次,甚至经常使用了这个东西。
我们知道,在一个有导航控制器的控制器P中加入了子控制器Q,然后添加了Q的view,这个Q就可以使用self.navigationController进行跳转,Q是利用的P的导航控制器进行跳转。也就是说Q的关于控制器的操作都被P给处理了。

那新加入一个问题:如果在Q中present出来一个界面R,那这个R的presentingViewController是谁呢?
是的,是P。此时已经出现了刚才说的问题,Q打开了R,但是R的presentingViewController却是P。这时因为Q是P的子控制器。

那么就可以解释上面两个图片的结果为什么是navi控制器和tabbar控制器了。因为使用标签控制器和导航控制器,都是显示的子控制器的内容,一个带导航栏,一个带标签栏。
所有的控制器都有childViewControllers属性,保存了所有的子控制器,但是有些特殊的控制器,比如UIPageViewController、UINavigationController、UITabBarController等,还有一个viewControllers属性,实际内容和childViewControllers一样。这些特殊的控制器实际上不展示主要内容,主要内容由子控制器展示。
所以刚才说,只要你学了UIKit用了UINavigationController和UITabBarController,你就在使用childViewControllers在做界面的管理了。

因此,当某个控制器有父控制器的时候,它的presentingViewController是父控制器的presentingViewController。

说这么多,又有毛用?

image.png

比如你做过转场动画,会用到这么个方法animateTransition(using transitionContext: UIViewControllerContextTransitioning),当然你首先要做的,就是从context里获取toViewcontroller和fromViewController。如果你的这个转场是个modalPresent,此时你获取到的fromViewController可不一定是调用present的那个控制器,所以你要根据这个VC来做一些动画,可能就要有问题了。

如果是带UINavigationController,需要通过nav.topViewController获取nav当前的控制器,如果是带UITabBarController,需要通过tab.selectedViewController获取tab当前的控制器。

四、如何强制指定presentingViewController就是打开自己的那个?

我们需要了解一下这两个属性:

@property(nonatomic,assign) BOOL definesPresentationContext;
@property(nonatomic,assign) UIModalPresentationStyle modalPresentationStyle;

modalPresentationStyle属性决定了将要present的控制器以何种方式展现,默认值为UIModalTransitionStyleCoverVertical。如果把一个控制器的definesPresentationContext属性设置为YES,那么在需要进行UIModalPresentationCurrentContext类型的跳转的时候,UIKit会使用视图层级内的这个控制器来进行跳转。

avc.definesPresentationContext = false
avc.modalPresentationStyle = .currentContext

大功告成!现在presentingViewController能够获取到我们期望的对象了。

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

推荐阅读更多精彩内容