当使用UINavigationController时,我的控制器的viewWillAppear或viewDidAppear方法不会被调用

这是球场。

  • 我有一个UIViewController子类,它在viewWillAppear和viewDidAppear方法中做了一些事情。
  • 我想在UINavigationViewController中嵌套这个视图控制器。
  • 根据视图层次的复杂性,我的控制器的两个方法viewWillAppearviewDidAppear可能不会被调用。

我应该怎么做才能确保这两个方法总是被调用,而不pipe我的视图层次?

“复杂”视图层次结构的示例:

 UIViewController subclass containing a UITabBarController |_ Each tab containing a UINavigationViewController |_ Each UINavigationController controller containing a custom UIViewController 

当您将TabBarController作为模式视图呈现时,将调用TabBarController的viewWillAppearviewDidAppear方法,但不嵌套在UINavigationViewControllers下嵌套的自定义UIViewController。

注意:这是在2013年编写的。如今iOS处理视图层次结构的方式的改变可能使得这个解决scheme无用和/或危险。 所以使用风险自负。

原始解决scheme在UINavigationController下嵌套一个自定义UIViewController时,可能不会调用自定义viewController的viewWillAppear和viewDidAppear,这取决于视图控制器层次结构的复杂性(认为模态视图,导航控制器内部的标签视图控制器…)。 所以如果你发现自己处在这种情况下,你可以做什么来确保这两种方法被称为?

答案…

使用UINavigationControllerDelegate方法

这是一个非常优雅的实现方法,它不依赖于控制器何时将被导航控制器加载的任何假设。

有两种方法可用:

 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated 

这是代码将如何改变。

你需要声明你的CustomViewController实现了UINavigationControllerDelegate协议:

 @interface CustomViewController : UIViewController <UINavigationControllerDelegate> 

您需要将您的CustomViewController设置为初始化它的UINavigationController的委托。

最后,您还必须将您的UINavigationControllerDelegate方法的自定义实现添加到您的CustomViewController类实现中。 例如,您可以实现navigationController:willShowViewController:animated:方法,以便:

  • 当UINavigationController即将显示视图控制器本身时,您的viewWillAppear方法被调用
  • 当UINavigationController即将显示另一个视图控制器时,如果此视图控制器实现UINavigationViewControllerDelegate方法,则将UINavigationController的委托设置为此其他视图控制器。

项目清单

 - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([viewController isEqual:self]) { [viewController viewWillAppear:animated]; } else if ([viewController conformsToProtocol:@protocol(UINavigationControllerDelegate)]){ // Set the navigation controller delegate to the passed-in view controller and call the UINavigationViewControllerDelegate method on the new delegate. [navigationController setDelegate:(id<UINavigationControllerDelegate>)viewController]; [[navigationController delegate] navigationController:navigationController willShowViewController:viewController animated:YES]; } } 

navigationController:didShowViewController:animated:可以简单实现如下:

 - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated { if ([viewController isEqual:self]) { [self viewDidAppear:animated]; } } 

这种方法的好处在于,您完全依赖于UINavigationViewController应该工作的方式,而且您只需在正确的时间打电话。 它还允许您在调用viewWillAppear方法之前,在导航控制器层次结构中上下移动代理团队。

再次为简单的层次结构,这可能不是必需的。 但是,如果您发现自己处于viewWillAppearviewDidAppear方法未被调用的状态,则您现在知道该怎么做…

这将发生的一个原因是,如果你重写viewDidAppear:在你的UINavigationController子类中,不要调用[super viewDidAppear:animated];

现在是2015年,您可能不需要像接受的答案中那样使用UINavigationControllerDelegate方法。 如果您有任何错字或复制/粘贴错误,请仔细检查您的代码。

最近我碰到一个问题,那就是viewDidAppear在复制/粘贴后不再被调用。 阅读@亚尔的回答后,我做了一个viewDidAppear在我的代码search,发现[super viewDidAppear:animated]; 被错误地称为viewWillAppear

 -(void)viewWillAppear:(BOOL)animated { [super viewDidAppear:animated]; //... ^^^ } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // this is never called :( } 

只要在这里分享这个发现,以防人们遇到同样的问题。

应该按照如下方式进行:

请参阅(* 1)编辑

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { CustomViewController *controller = [[CustomViewController alloc] initWithNibName:nil bundle:nil]; UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller]; [controller release]; self.window.rootViewController = navController; //(*1) [self.window makeKeyAndVisible]; [navController release]; return YES; }