自定义容器控制器

自定义容器控制器

容器控制器可以添加并管理子视图控制器,它需要管理子视图控制器视图的进入、移除界面;当然我们也可以自定义容器控制器,因为系统给每一个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切换时生命周期函数执行的顺序

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的修养

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值