自定义容器控制器
容器控制器可以添加并管理子视图控制器,它需要管理子视图控制器视图的进入、移除界面;当然我们也可以自定义容器控制器,因为系统给每一个UIViewController提供了一个childViewController属性。但是首页我们明白自定义容器VC的目的是什么? 目的是在同一个屏幕上展示不同的VC的需求
为了实现这一目的,我们在自定义容器VC时,需要考虑以下内容
- 不同VC切换的效果
- 容器VC可以正常显示子VC的view
- 子VC可以接收viewDidLoad:等生命周期方法、屏幕旋转的方法等
下面我们看看怎么自定义一个容器控制器
1、自动义容器控制器
如果你要自定义一个容器控制器,那么自定义的容器控制器需要负责以下事情:
向容器控制器添加一个子视图控制器时
- 调用自己的
addChildViewController:
添加子视图控制器
addChildViewController
自动调用了willMoveToParentViewController:
,所以我们需要收到调用didMoveToParentVIewController:
- 调用
addSubview:
将子视图控制器的view添加到容器视图控制器,显示到界面, - 子视图控制器调用
didMoveToParentVIewController:
[self addChildViewController:vc]; // "will" is called for us
[self.view addSubview: vc.view];
[vc didMoveToParentViewController:self];
vc.view.frame = // whatever, or use constraints
将容器控制器的子视图控制器移除
- 子视图控制器调用
willMoveToParentViewController
: - 移除子视图控制器的视图
- 子视图控制器调用
removeFromParentViewCntroller
removeFromParentViewController
自动调用了didMoveToParentVIewController
,所以需要手动调用willMoveToParentViewController:
[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];
2、处理切换时的过渡动画
当需要用一个子视图控制器替换另一个子视图控制器时,可以将子视图控制器的添加和删除的动画合并到转场动画过程中。前提,请确保两个子视图控制器都是容器视图控制器的子视图控制器。
- (void)transitionFromViewController:(UIViewController *)fromViewController
toViewController:(UIViewController *)toViewController
duration:(NSTimeInterval)duration
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
这个方法用来处理两个子视图控制器的转场动画,下面我们看看各个参数的作用
- fromViewController:当前显示的子视图控制器,将被替换
- toViewController:将要显示的子视图控制器
- duration:交换动画持续的时间,单位秒
- options:动画的方式
- animations:动画Block ,可以设置视图的frame
- completion:完成后执行的Block,在这里可以调用fromVC的
removeFromParentViewCOntroller:
,和toVC的didMoveToParentVIewController:
tovc.view.translatesAutoresizingMaskIntoConstraints = NO;
// must have both as children before we can transition between them
[self addChildViewController:tovc]; // "will" called for us
// when we call remove, we must call "will" (with nil) beforehand
[fromvc willMoveToParentViewController:nil];
[self transitionFromViewController:fromvc
toViewController:tovc
duration:0.4
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
tovc.view.frame = oldVC.view.frame;
fromvc.view.frame = endFrame;
}
completion:^(BOOL done){
// when we call add, we must call "did" afterwards
[tovc didMoveToParentViewController:self];
[fromvc removeFromParentViewController];
// "did" is called for us
}];
虽然transitionFromVieewController:toViewController:duration:animations:comletion可以在自定义容器vc的时候处理两个子视图vc切换时的转场动画,但是能力有限,iOS7之后,苹果给出了另一种方案具体细节可以参看喵神的WWDC 2013 Session笔记 - iOS7中的ViewController切换文章
3、子视图的生命周期方法
一般容器控制器会将view的state()转发给子视图控制器,但是有时我们想自己管理子视图的state变化,我们可以重写shouldAutomaticallyForwardAppearanceMethods
方法,返回no
- (BOOL) shouldAutomaticallyForwardAppearanceMethods {
return NO;
}
但是,如果我们手动管理子视图控制器view的state变化,我们需要使用下面的方法子视图控制器可以知道它的view状态是appear或者disappear
- (void)beginAppearanceTransition:(BOOL)isAppearing
animated:(BOOL)animated;
- (void)endAppearanceTransition;
beginAppearanceTransition:
当参数isAppearing
设置为 YES ,会触发viewWillAppear:
;设置为 NO,则触发viewWillDisappear:
;endAppearanceTransition
方法会基于我们传入的isAppearing
来调用viewDidAppear:
以及viewDidDisappear:
方法
下面我们看一个例子,一个contanerVC里面有vc和vc1,通过按钮来切换vc
//例子1:添加vc,移除vc1
-(void)btnPressed:(id)sender
{
UIViewController *vc = self.vc;
NSLog(@"%@",[vc class]);
[self addChildViewController:vc];
[vc beginAppearanceTransition:YES animated:YES];
[self.view addSubview:vc.view];
[vc endAppearanceTransition];
[self didMoveToParentViewController:vc];
if (self.vc1)
{
NSLog(@"%@",[self.vc1 class]);
[self.vc1 beginAppearanceTransition:NO animated:YES];
[self.vc1.view removeFromSuperview];
[self.vc1 endAppearanceTransition];
[self.vc1 willMoveToParentViewController:self];
[self.vc1 removeFromParentViewController];
}
}
输出
2021-03-30 08:09:12.889237+0800 TX[2635:28009] childViewController
2021-03-30 08:09:12.889658+0800 TX[2635:28009] -[childViewController viewWillAppear:]
2021-03-30 08:09:12.890186+0800 TX[2635:28009] -[childViewController viewDidAppear:]
2021-03-30 08:09:12.890345+0800 TX[2635:28009] childViewController1
2021-03-30 08:09:12.890490+0800 TX[2635:28009] -[childViewController1 viewWillDisappear:]
2021-03-30 08:09:12.890655+0800 TX[2635:28009] -[childViewController1 viewDidDisappear:]
例子2:
-(void)btnPressed2:(id)sender
{
if (self.vc)
{
//vc即childViewController
NSLog(@"%@",[self.vc class]);
[self.vc beginAppearanceTransition:NO animated:YES];
[self.vc.view removeFromSuperview];
[self.vc endAppearanceTransition];
[self.vc willMoveToParentViewController:self];
[self.vc removeFromParentViewController];
}
UIViewController *vc = self.vc1;
NSLog(@"%@",[vc class]);
[self addChildViewController:vc];
[vc beginAppearanceTransition:YES animated:YES];
[self.view addSubview:vc.view];
[vc endAppearanceTransition];
[self didMoveToParentViewController:vc];
}
输出
2021-03-30 08:10:02.201438+0800 TX[2635:28009] childViewController
2021-03-30 08:10:02.201649+0800 TX[2635:28009] -[childViewController viewWillDisappear:]
2021-03-30 08:10:02.201976+0800 TX[2635:28009] -[childViewController viewDidDisappear:]
2021-03-30 08:10:02.202381+0800 TX[2635:28009] childViewController1
2021-03-30 08:10:02.202752+0800 TX[2635:28009] -[childViewController1 viewWillAppear:]
2021-03-30 08:10:02.203222+0800 TX[2635:28009] -[childViewController1 viewDidAppear:]
在例子1中,我们先添加vc,在移除vc1的时候,并且使用beginAppearanceTransition:animated:和endAppearanceTransition来管理子视图的生命周期, vc1的viewWillDisappear和viewDidDisappear在vcviewWillAppear和viewDidAppear后面调用。
例子2中我们将vc移除放在前面,vc1的添加放在后面,这时先调用vc的viewWillDisappear和viewDidDisappear,在调用vc1的viewWillAppear和viewDidAppear
–结论,我们在实际开发中最好先移除在添加,这样比较符合两个VC切换时生命周期函数执行的顺序