Pousser le controller de vue dans viewDidAppear ne fonctionne pas

Étapes à suivre pour reproduire

1) Créez un controller de navigation et 3 controllers de vue.

firstViewController.m:

- (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; NSLog(@"DEBUG: first screen did appear"); [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"secondScreen"] animated:NO]; } 

secondViewController.m:

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; NSLog(@"DEBUG: second screen did appear"); [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES]; } 

thirdViewController.m:

 - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; NSLog(@"DEBUG: third screen did appear"); } 

2) make firstViewController (alias firstScreen dans storyboard) le controller de vue racine du controller de navigation.

3) Lancez l'application et notez que la barre de navigation a été mise à jour pour afficher le titre du troisième écran, mais affiche toujours le contenu du second écran.

Remarques

J'ai essayé d'utiliser UINavigationControllerDelegate -( void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated méthode -( void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated car il semble se triggersr après la méthode viewDidAppear , mais il n'a pas corrigé le problème.

J'ai également essayé de régler manuellement les viewControllers du controller de viewControllers pensant qu'il ne viewControllers la viewControllers «this view controller is active» et que le push problématique fonctionnerait, mais ce n'est pas le cas.

Solution

La seule solution que j'ai pu find consistait à utiliser un appel différé pour pousser le controller de vue désiré dans secondViewController.m :

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 250 * USEC_PER_SEC), dispatch_get_main_queue(), ^{ [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES]; }); 

Problème

Je voudrais comprendre pourquoi cela ne fonctionne pas comme prévu. Basé sur d'autres réponses SO que j'ai vues sur des questions semi-pertinentes, cela peut avoir quelque chose à voir avec une boucle d'exécution, mais je ne peux pas confirmer ou infirmer cela (cela semble possible puisque l'envoi du push le permet).

Quelqu'un d'autre avec plus de connaissances / expérience peut-il m'éclairer?

Merci!

C'est une question intéressante. Je suis assez confiant que si vous deviez mettre en animated:YES en poussant le second controller de vue dans firstViewController.m , l'état final de l' IU ressemblerait à celui attendu, avec le contenu et le titre du troisième écran correctement visibles.

Cependant, ce n'est clairement pas l'effet de transition que vous visez. Et pourquoi le drapeau animated fait-il un iota de différence de toute façon?

Si vous définissez un point d' -viewDidAppear: dans -viewDidAppear: et regardez la trace de la stack pour le cas où animated == YES et animated == NO , il me semble comme quand animated == NO , -viewDidAppear: est invoqué pendant une vue opération de layout dans UINavigationController . Mon argent est que la raison pour laquelle vous avez une vue finale semble incorrecte; effectuer une poussée maintenant le ferait avant que la poussée précédente soit complètement terminée.

C'est ici UINavigationController les considérations de boucle d'exécution. Nous voulons que la UINavigationController la vue d' UINavigationController (qui se produit sur le cycle de boucle actuel de la boucle d'exécution principale) se termine avant de requestr la prochaine poussée. Un moyen simple d'y parvenir consiste à mettre la poussée en queue sur le prochain cycle de la boucle d'exécution principale. Un retard fera certainement l'affaire (je crois qu'un timeout de 0 est suffisant pour retarder le prochain cycle de boucle d'exécution, donc vous pouvez essayer de replace 250 * USEC_PER_SEC par 0 ). Une autre façon serait d'envoyer l'action dans la queue principale:

 dispatch_async(dispatch_get_main_queue(), ^{ [self.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"thirdScreen"] animated:YES]; }); 

Donc, ma réponse est un peu spéculative, mais basée sur des preuves. Il est légèrement insatisfaisant que, lors de l'exécution des transitions UINavigationController , -viewDidAppear: indique seulement la fin réelle de la transition quand elle est animée, mais cela semble être le cas.

Je ne sais pas pourquoi cela ne fonctionne pas exactement, a probablement à voir avec le calendar. Mais pour pousser plusieurs controllers de vue sur un controller de navigation, la méthode préférée consiste à utiliser setViewControllers:animated:

 NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[navigationController viewControllers]]; [viewControllers addObjectsFromArray:viewControllersToPush]; [navigationController setViewControllers:viewControllers animated:YES];