Cuando se utiliza un UINavigationController no se llaman los methods viewWillAppear ni viewDidAppear de mi controller

Aquí está el campo.

  • Tengo una subclass UIViewController que hace algo en sus methods viewWillAppear y viewDidAppear.
  • Quiero anidar este controller de vista en UINavigationViewController.
  • Dependiendo de la complejidad de la jerarquía de vista, no se pueden viewWillAppear los dos methods viewWillAppear y viewDidAppear de mi controller.

¿Qué debo hacer entonces para asegurarme de que estos dos methods siempre se llaman independientemente de mi jerarquía de vistas?

Ejemplo de una jerarquía de vista "compleja":

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

Cuando presenta TabBarController como una vista modal, se viewDidAppear methods viewWillAppear y viewDidAppear del TabBarController, pero no los de los UIViewControllers personalizados nesteds en UINavigationViewControllers.

NOTA: esto se escribió en 2013. Los cambios en la forma en que iOS maneja las jerarquías de vista en la actualidad pueden hacer que esta solución sea inútil o peligrosa. Entonces, use bajo su propio riesgo.

Respuesta original Al anidar un UIViewController personalizado bajo un UINavigationController, los methods viewWillAppear y viewDidAppear del viewController personalizado pueden no invocarse dependiendo de la complejidad de su jerarquía del controller de vista (piense en las vistas modal, el controller de navigation dentro del controller de vista de pestaña …). Entonces, si te encuentras en esta situación, ¿qué puedes hacer para garantizar que se llamen a estos dos methods?

La respuesta…

Utilice los methods UINavigationControllerDelegate

Este es un método muy elegante para implementar ya que no se basa en suposiciones sobre cuándo el controller de carga cargará el controller.

Hay dos methods disponibles:

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

Así es como cambiará el código.

Debe declarar que CustomViewController implementa el protocolo UINavigationControllerDelegate:

 @interface CustomViewController : UIViewController <UINavigationControllerDelegate> 

Debe configurar su CustomViewController como el delegado del UINavigationController donde lo inicializa.

Por último, también debe agregar su implementación personalizada de los methods UINavigationControllerDelegate a la implementación de class CustomViewController. Por ejemplo, puede implementar el método navigationController:willShowViewController:animated: para que:

  • cuando el UINavigationController está a punto de mostrar el controller de vista en sí, se llama a su método de vistaWillAppear
  • cuando UINavigationController está a punto de mostrar otro controller de vista, el delegado del UINavigationController se establece en este otro controller de vista, siempre que este controller de vista implemente el método UINavigationViewControllerDelegate.

Producto en la list

 - (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]; } } 

Y el navigationController:didShowViewController:animated: puede implementarse simplemente de la siguiente manera:

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

El beneficio de este enfoque es que confía únicamente en la forma en que se supone que funciona UINavigationViewController y realiza sus llamadas justo en el momento adecuado. También le permite pasar la delegación a medida que sube y baja la jerarquía del controller de navigation justo antes de llamar al método viewWillAppear.

De nuevo para una jerarquía simple, esto puede no ser necesario. Pero si alguna vez te encuentras en una situación en la que no se llama a tus methods viewWillAppear y viewDidAppear , ahora sabes qué hacer …

Una razón por la que esto sucederá es si anula viewDidAppear: en su subclass UINavigationController y no llama [super viewDidAppear:animated];

Es 2015 ahora y probablemente no necesite utilizar los methods UINavigationControllerDelegate como en la respuesta aceptada. Simplemente revise cuidadosamente su código si tiene algún error tipográfico o de copyr / pegar.

Me encontré con un problema últimamente que viewDidAppear ya no se llama después de algunos copyr / pegar. Después de leer la respuesta de @ Yar, hice una búsqueda en viewDidAppear en mi código y encontré que [super viewDidAppear:animated]; fue llamado erróneamente en la vista viewWillAppear :

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

Solo comparte este hallazgo aquí en caso de que las personas se topen con el mismo problema.

debe hacerse de la siguiente manera:

Ver (* 1) editar

 - (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; }