前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >跟iOS UI的捉迷藏,真的能够获取到self.presentingViewController

跟iOS UI的捉迷藏,真的能够获取到self.presentingViewController

作者头像
粲然忧生
发布2022-08-02 15:07:45
6270
发布2022-08-02 15:07:45
举报
文章被收录于专栏:工程师的分享

近期,在做一个有趣的实验,研究presentViewController和dismissViewController的对应关系,这里发现了一个有趣的现象,通常情况下,两者配对使用,如果连续两次presentViewController会出现一个warning:同时第二个会有很大概率加载失败(如何让载成功的例子后面会提到)

两次dismissViewController则会出现:

不要纠结错误的信息,总之是不对的

配对使用是一个很好的习惯,也是保持布局稳定,这里有个知识点,dismissViewController怎么使用

首先看官方文档:

Dismisses the view controller that was presented modally by the view controller. The presenting view controller is responsible for dismissing the view controller it presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal. If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.

可以简单归纳为两点:

第一点:谁present出来的控制器,谁负责把它dismiss掉,但是如果你在被present出来的控制器中调用dismiss的话,UIKit会自动让它的presenting控制器(找到谁把它present出来的)去执行dismiss。

第二点:如果你present了一系列的控制器,那么系统会把被present出来的控制器放在一个栈中,当处在底层的控制器执行dismiss的时候,在它之后被present出来的控制器都会被移除,只有栈顶上的控制器会有dismiss动画。

关于第一点,很重要,意思是说父VC和自身都可以调用dismiss,系统会自动去找presenting的控制器

关于第二点,如果业务十分复杂,present了很多控制器,想一下子回到栈顶的控制器,则可以

代码语言:javascript
复制
UIViewController *presentingVc = self.presentingViewController;
while (presentingVc.presentingViewController) {
    presentingVc = vc.presentingViewController;
}
if(presentingVc){
    [presentingVc dismissViewControllerAnimated:YES completion:nil];
}

具体可以参考:https://www.jianshu.com/p/0a27dc2028e3

重点说第一点

事实上,presentViewController的功能要复杂的多

苹果官方的�ViewController Programming Guide中关于Presenting a View Controller的部分是这样说的

The view controller that calls the presentViewController:animated:completion: method may not be the one that actually performs the modal presentation. The presentation style determines how that view controller is to be presented, including the characteristics required of the presenting view controller.

The View Controller Hierachy关于Presented View Controller中找到了这样一句话:

When you present a view controller, UIKit looks for a view controller that provides a suitable context for the presentation. In many cases, UIKit chooses the nearest container view controller but it might also choose the window’s root view controller.

也就是说,在调用presentViewController:animated:completion:方法时,真正作为跳转的容器并不一定是调用这个方法的view controller,而是取决于modalPresentationStyle。例如,当被present出来的控制器的modalPresentationStyle = UIModalPresentationFullScreen时,执行当前present事件的控制器必须是一个全屏控制器,如果当前执行的控制器不是一个全屏的控制器,它将在视图层级结构中找到一个全屏的父类控制器去执行present事件。也就是说如果A 执行present B,那么B.presentingViewController不一定是A。比如你当前的控制器A在导航nav中,A present B之后,实际上B.presentingViewController指向的是nav而不是A。

流程如下:

   self.presentingViewController,它指向的是把当前控制器present出来的控制器或者是把当前控制器的最上层的父类present出来的控制器,如果modalPresentationStyle都符合条件,那么到底是谁呢,UIKit可能会选择最近起,也会选择rootview,总之不确定。

这就会影响self.presentingViewController获取,有时业务要求还是需要获取代码上presentVC的控制器

那么能不能把这个变量确定呢

解决这个需求,我们需要用到一对属性

代码语言:javascript
复制
@property(nonatomic,assign) BOOL definesPresentationContext;
@property(nonatomic,assign) UIModalPresentationStyle modalPresentationStyle;

modalPresentationStyle属性决定了将要present的控制器以何种方式展现,默认值为UIModalTransitionStyleCoverVertical

definesPresentationContext就有点神奇了,他的注释文档是这么写的

Determines which parent view controller's view should be presented over for presentations of type UIModalPresentationCurrentContext. If no ancestor view controller has this flag set, then the presenter will be the root view controller.

简单来说,如果把一个控制器的definesPresentationContext属性设置为YES,那么在需要进行UIModalPresentationCurrentContext类型的跳转的时候,UIKit会使用视图层级内的这个控制器来进行跳转。

看到这里是不是有点晕,不急,从头再看一遍,后面就好理解了

FirstViewController中加入下面的代码

代码语言:javascript
复制
[self.navigationController setDefinesPresentationContext:YES];

并在跳转的时候设置目标控制器的modalPresentationStyle

代码语言:javascript
复制
[nav setModalPresentationStyle:UIModalPresentationCurrentContext];
[self presentViewController:tempVC2 animated:YES completion:nil];

这样

tempVC2.presentingViewController就是FirstViewController了

参考文章如下:

https://www.jianshu.com/p/6b79cf270796

https://www.jianshu.com/p/0a27dc2028e3

拓展知识:

事实上,还有一个问题没有解决,如果连续两次presentViewController会出现一个warning:同时第二个会有很大概率加载失败,那能不能成功呢,虽然这个需求很奇怪,但在复杂逻辑中,加入了通知等响应性的内容时,很难保证不出现这样的情况,而一旦第二个无法加载成功,则会开发同学产生很深的困扰,也会把难以预期的UIBug暴漏给用户

事实上,设置了上文的两个属性后,再连续加载,你会发现连续两次presentViewController,第二个控制器加载成功,但同时会有一个新的warning:Presenting view controllers on detached view controllers is discouraged

从两个warning可以看出,iOS对此虽然包容,但没事尽量上出现这样的情况,不然总会有潜在的问题,比如下面这个问题:

这个加载出来的控制器怎么dismiss掉,先去掉两个错误答案

[tempVC2 dismissViewControllerAnimated:NO completion:nil];

和在FirstViewController中调

[self dismissViewControllerAnimated:NO completion:nil];

都不行

欢迎大家一起讨论

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-07-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档