IOS开发关于容器控制器的一些思考

背景

在IOS开发中,容器类视图控制器有UINavigationController、UITabBarController以及UISplitViewcontroller这么三种。当我们自己要去实现一个视图控制器容器的时候,我们需要做以下操作:

1、我们需要创建一个subViewController

2、调用[self addChildViewController:self.controller1];

3、执行[self didMoveToParentViewController:self.controller1];通知加载结束

4、将subViewController的view添加到容器控制器上,并且调整其大小到一个合适的大小

以上4个步骤就是自定义容器控制器,并且添加一个视图控制器的步骤。但可以思考下,为什么要这么做呢?是不是所有的容器控制器内部都是这么做的呢?UINavigationController是我们平时用的比较多的容器类,我记得我们平时在使用UINavigationController的时候就是调用了pushViewController然后就展示了页面,调用popViewController就销毁了页面,并没有使用过上面的代码,是不是UINavigationController里面封装了这些东西呢?与此同时,我们在使用viewController的时候,并没有像上面那样设置其view的大小,为什么它的大小就是屏幕大小呢?

带着这些疑问,开始研究,容器视图控制器的实现。

window说起

在我们平时写项目的时候我们可能不会注意到viewController的里这个自带View的大小,因为我们会直接new,然后push,然后我们会理所当然的认为它的大小和屏幕的大小是一样大的,但真的是这样吗?你可能会说,是啊,我都测试过了!那不同的机型苹果里面是如何确定这个视图大小的呢?我们也没在外部配置过,这个又该怎么解释呢?

这个view的大小,或者我们容器中push的viewController的view的大小肯定是被设置过的,但是怎么设置的呢?带着这个疑问,我开始翻看源代码,在UINavigationController中,我找到了loadView,代码里有创建view大小的代码,如下: 

再查看loadView是从哪里被调用的,发现是在ViewController这个基类的view的setter方法中被调用的,代码如下:

可以从上面的代码看到,如果已经创建过view,那直接返回这个view的内部实例对象,否则的话会调用loadView,然后调用我们熟悉的viewDidLoad方法,也就是说,当我们使用这个view的时候,那么一定会先创建一个view,保证它是存在的。

看下源代码的注释:

可以看到,和上面的解释是一模一样的。但上面代码创建出来的View大小是320x480的,大小也不是我手机屏幕的大小呀?那这个时候,第一次使用这个view的地方就成了突破口。

我们知道,APP启动以后,我们会设置window的rootViewController,会不会是那里呢?继续找UIWindow的原代码:

我们发现,在其rootViewController的setter方法中,有设置这个rootViewController的大小,但这个frame的大小是self.bounds,那么这个时候我们就应该想到了,为什么我们在写项目的时候,会先初始化这个window,而且是这么写的:

在初始的时候,window的大小就已经被指定为screen的bounds的大小了,然后根控制器的大小也就顺理成章的设置成了窗口这么大。如果我们在这里将window的大小设置为其他大小呢?经过测试果然是,整个app的view大小都会随着变化。

OK,到这里我们就明白了,原来我们在写VC的时候,为什么不需要设置他的视图大小是因为:

1、在设置Window的时候,其设置了根视图控制器的大小

2、当根视图为UINavigationController的时候,每当我们push或者pop的时候,其都会将这些子ViewController的大小设置的和UINavigationController的View一样大小。

上面的问题到现在就可以解答了,UINavigationController内部确实是用了addChildViewController这个方法,并且在内部其设置了子VC的frame大小,所以在当我们push或者pop的时候,不需要管其他的操作。

addChildViewController是个什么玩意?猜测应该是内部维护了一个数组,数组里存放ViewController,是不是这样呢?

果然没错,其内部维护了一个可变的数组,而且里面还调用了willMoveToParentViewController,怪不得网上说加视图控制器是不需要加这个,因为里面已经加过了,这些应该是明白了吧。

一张图说明问题:

突然产生的疑问

既然容器视图控制器内部实际上是维护了一个数组,我们知道,ViewController继承于NSObject,最终展示的是他的view,那它的作用是什么?没有它行不行呢?

我是这么理解的,我觉得他就是一个视图管理器,管理着这个view,真正展示和这个ViewController是没有半点关系的,我们哪怕就是一个很小的视图,我也可以新建一个viewController来管理,到时候只需要将其view加到parentView上展示就可以了。这也就能理解,为什么viewController中有这么几个管理视图的方法:

  1. viewDidLoad
  2. viewWillAppear
  3. viewDidAppear
  4. ...

这几个方法就是通知我们,此时viewController维护的这个view的状态。

之前就说viewController就是严格验证MVC设计模式来做的,viewController就是担任这个C,视图View就是这个V,因为我们的button一般响应方法都是放在viewController中来写的,而且几乎所有的业务都是放在ViewController中的,view只负责展示而已。

明白了以上这些,自己再去写一个容器视图控制器应该就很简单了吧。无非就是这么3步:

1、维护一个数组,用来保存要管理的子viewController

2、要展示的时候,将一个合适的子viewController的view放add到container上

3、不展示的时候将其移除,并且删除子viewController

留下一个思考题

在使用TabBarViewController的时候,我发现viewDidLoad只被调用了一次,以后就不会被调用了,这个是怎么做的呢?

感兴趣的同学可以留言哈~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值