围绕addChildViewController的学习

关于iOS界面布局的层级关系,以及ViewController的嵌套切换,一直都让我模糊不清,其中最常见的函数就是addChildViewController。所以就围绕这个函数,来把ViewController之间的关系摸得更清楚一点。

先看这个函数的Apple官方解释。

/*
  If the child controller has a different parent controller, it will first be removed from its current parent
  by calling removeFromParentViewController. If this method is overridden then the super implementation must
  be called.
*/
- (void)addChildViewController:(UIViewController *)childController NS_AVAILABLE_IOS(5_0);

这个函数从iOS5.0时就已经支持了。当添加的childController已经被添加到其他的父类Controller时,将会首先调用removeFromParentViewController方法从其父类上移除。当该方法需要重载,需要调用其父类方法。

addChildViewController官方标准写法是:官方文档可查

ChildViewController *child = [[ChildViewController alloc] init];
[self addChildViewController:child];
[self.view addSubview:child.view];
[child didMoveToParentViewController:self];

上面的代码,先创建了子类vc的实例,并将其添加到父类vc中,作为其子类vc。然后添加子类的view到父类的view中,最后调用了didMoveToParentViewController函数。

这里需要注意的是,在addChildViewController调用时,不需要调用willMoveToParentViewController函数。因为官方文档已经说的很清楚了。

/*
  These two methods are public for container subclasses to call when transitioning between child
  controllers. If they are overridden, the overrides should ensure to call the super. The parent argument in
  both of these methods is nil when a child is being removed from its parent; otherwise it is equal to the new
  parent view controller.

  addChildViewController: will call [child willMoveToParentViewController:self] before adding the
  child. However, it will not call didMoveToParentViewController:. It is expected that a container view
  controller subclass will make this call after a transition to the new child has completed or, in the
  case of no transition, immediately after the call to addChildViewController:. Similarly
  removeFromParentViewController: does not call [self willMoveToParentViewController:nil] before removing the
  child. This is also the responsibilty of the container subclass. Container subclasses will typically define
  a method that transitions to a new child by first calling addChildViewController:, then executing a
  transition which will add the new child's view into the view hierarchy of its parent, and finally will call
  didMoveToParentViewController:. Similarly, subclasses will typically define a method that removes a child in
  the reverse manner by first calling [child willMoveToParentViewController:nil].
*/

大概翻译如下:

  1. willMoveToParentViewController和didMoveToParentViewController函数是为子类vc开放使用的,重载这两个方法时,一定要调用其父类的该方法。在子类vc被移除时,父类的该函数参数是nil值,否则该函数参数就是其父类。

  2. addChildViewController会在添加子类时调用[child willMoveToParentViewController:self],所以我们不必手动调用这个willXXX函数。但是不会自动调用didMoveToParentViewController,所以在transition切换或者调用addChildViewController后,必须立刻要调用didMoveToParentViewController。

  3. removeFromParentViewController函数调用前,不会自动调用[self willMoveToParentViewController:nil]。所以需要我们手动来调用。

  4. 在子类vc做transition切换时,首先需要调用addChildViewController函数,然后把子类vc的view添加到父类的view中,在动画切换结束时,需要手动调用didMoveToParentViewController函数。


上面的代码实例是如何添加子类vc到父类上。其实也谈及到如何从父类移除子类vc的步骤。

下面给出官方标准移除子类vc的写法:官方文档可查

[child willMoveToParentViewController:nil];
[child.view removeFromSuperview];
[child removeFromParentViewController];

最后拿来官方的一个例子,混合说明下:

- (void)cycleFromViewController: (UIViewController*) oldVC
               toViewController: (UIViewController*) newVC {

   // 为transition切换做准备
   [oldVC willMoveToParentViewController:nil];
   [self addChildViewController:newVC];
 
   // view切换前后的frame大小设置
   newVC.view.frame = [self newViewStartFrame];
   CGRect endFrame = [self oldViewEndFrame];
 
   [self transitionFromViewController: oldVC toViewController: newVC
        duration: 0.25 options:0
        animations:^{
            // view变换的最终位置
            newVC.view.frame = oldVC.view.frame;
            oldVC.view.frame = endFrame;
        }
        completion:^(BOOL finished) {
           // 移除oldVC,newVC发出变换完成通知
           [oldVC removeFromParentViewController];
           [newVC didMoveToParentViewController:self];
        }];
}

终于弄清楚了什么时候该用willMoveToParentViewController和didMoveToParentViewController函数~

为什么需要这样一种复杂的方式来添加切换view呢,而不是直接将view放在superView上面呢?

摘自唐巧的博客片段截图:

效果

附上自己写的测试代码:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //初始化ThirdViewController
    ThirdViewController *thirdViewController = [[ThirdViewController alloc]initWithNibName:@"ThirdViewController" bundle:nil];
    thirdViewController.navigationItem.title = @"third";
    self.navThird = [[UINavigationController alloc]initWithRootViewController:thirdViewController];
    [self addChildViewController:self.navThird];
    
    //初始化ChildViewController
    ChildViewController *childViewController = [[ChildViewController alloc]initWithNibName:@"ChildViewController" bundle:nil];
    self.navChild = [[UINavigationController alloc]initWithRootViewController:childViewController];
    [self addChildViewController:self.navChild];
    
    [self.view addSubview:self.navChild.view];
    [self.navChild didMoveToParentViewController:self];
    
    
    //-----------------------------------
    
    __weak typeof(self) weakSelf = self;
    thirdViewController.block = ^{
        [weakSelf transitionFromViewController:weakSelf.navThird toViewController:weakSelf.navChild duration:0.4 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
            
        } completion:^(BOOL finished) {
            [weakSelf.navChild didMoveToParentViewController:weakSelf];
        }];
    };
    
    childViewController.block = ^{
        [weakSelf transitionFromViewController:weakSelf.navChild toViewController:weakSelf.navThird duration:0.4 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^{
            
        } completion:^(BOOL finished) {
            [weakSelf.navThird didMoveToParentViewController:weakSelf];
        }];
    };
    
}

本篇结束,这其中的App——UI界面布局架构的知识还是很深的。需要继续挖掘。。

文章来自 http://skymonkey.cn/

高能广告区

暂无广告哦=^^=。继续看看其它文章吧!