今天遇到一个无比诡异的问题,原因在于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会不会受影响?还有没有更多的坑?不知道,实在是囧。