addChildViewController时viewDidAppear方法得不到调用的问题

今天遇到一个无比诡异的问题,原因在于viewDidLoad方法得到调用,而viewDidAppear方法得不到调用,导致viewDidAppear方法中的代码得不到执行。


下面写了个Demo,完整地模拟了这个场景。

首先是一个简单的故事板,其中AppDelegate的keyWindow是window,window的rootViewController是一个UINavigationController。如下:



NavigationController的rootViewController是ViewController。

ViewController上的Add按钮用于Add一个ChildViewController,其类为NewViewController类。

ViewController上的Remove按钮将之前Add上去的ChildViewController移除。


下面是ViewController类的代码:

#import "ViewController.h"
#import "NewViewController.h"
#import "AppDelegate.h"

@interface ViewController ()

@property (strong, nonatomic) NewViewController *nvc;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (IBAction)addChild:(id)sender {
    UIWindow *keywindow = [UIApplication sharedApplication].keyWindow;
    UIViewController *controller = keywindow.rootViewController;
    NSLog(@"%@", controller);
    
    self.nvc = [NewViewController new];
    [self.nvc willMoveToParentViewController:controller];
    [controller addChildViewController:self.nvc];
    self.nvc.view.frame = CGRectMake(100, 100, 100, 100);
    [controller.view addSubview:self.nvc.view];
    [self.nvc didMoveToParentViewController:controller];
}

- (IBAction)remove:(id)sender {
    [self.nvc.view removeFromSuperview];
    [self.nvc removeFromParentViewController];
}

还有NewViewController类的代码:

#import "NewViewController.h"

@implementation NewViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    NSLog(@"viewDidLoad");
    
    self.view.backgroundColor = [UIColor redColor];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    NSLog(@"viewDidAppear");
}

@end


跑下Demo,点击Add,点击Remove,再点击Add,再点击Remove。

输出的Log如下:

2014-09-25 01:35:41.824 ParentVCDemo[24025:60b] <UINavigationController: 0x8c86aa0>
2014-09-25 01:35:41.826 ParentVCDemo[24025:60b] viewDidLoad
2014-09-25 01:35:43.232 ParentVCDemo[24025:60b] <UINavigationController: 0x8c86aa0>
2014-09-25 01:35:43.233 ParentVCDemo[24025:60b] viewDidLoad


可以看到keyWindow的rootViewController是一个UINavigationController类,在该controller上addChildViewController,也就是NewViewController类的一个实例。

明显NewViewController中的viewDidLoad方法是被调用的,而viewDidAppear方法是得不到调用的。


于是,如果你把设置NewViewController的view背景颜色为红色的代码放到viewDidAppear方法中的话,你将死活见不到一个红色的视图出现在屏幕中。


原因:在UINavigationController类或其子类对象上addChildViewController,将会导致Child View Controller中的viewDidAppear方法得不到执行。


建议:

1. 由于NewViewController被removeFromSuperview时,没有对象持有它,该对象将会被释放,那么下一次Add一个NewViewController时,其viewDidLoad方法必定会被调用,因此不用担心viewDidLoad方法只调用一次的问题,可以将viewDidAppear方法中的代码移到viewDidLoad方法中执行。

2. 获取正确的keyWindow,或者获取keyWindow中正确的controller,例如我们可以判断keyWindow的rootViewController是不是UINavigationController类或其子类,如果是的话,可以获取该导航控制器的topViewController。例如这样:

    UIWindow *keywindow = [UIApplication sharedApplication].keyWindow;
    UIViewController *controller;
    if ([keywindow.rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController *naviController = (UINavigationController *)keywindow.rootViewController;
        controller = naviController.topViewController;
    }
    else {
        controller = keywindow.rootViewController;
    }

但是UITabBarController会不会受影响?还有没有更多的坑?不知道,实在是囧。



快两点了,晚安。就不骚扰某些人了,哈哈。


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。