J'ai une application dans laquelle je dois supprimer une vue de la pile d'un UINavigationController et la remplacer par une autre. La situation est que la première vue crée un élément modifiable, puis se remplace par un éditeur pour l'élément. Quand je fais la solution évidente dans la première vue:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];
[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];
J'ai un comportement très étrange. Habituellement, la vue de l'éditeur apparaîtra, mais si j'essaie d'utiliser le bouton de retour de la barre de navigation, j'obtiens des écrans supplémentaires, certains vides et d'autres tout simplement foutus. Le titre devient également aléatoire. C'est comme si la pile de navigation était complètement arrosée.
Quelle serait la meilleure approche à ce problème?
Merci, Matt
la source
L'approche suivante me semble plus agréable et fonctionne également bien avec ARC:
UIViewController *newVC = [[UIViewController alloc] init]; // Replace the current view controller NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]]; [viewControllers removeLastObject]; [viewControllers addObject:newVC]; [[self navigationController] setViewControllers:viewControllers animated:YES];
la source
if(indexPath.row == 0){UIViewController *newVC = [[UIViewController alloc] init];newVC = [self.storyboard instantiateViewControllerWithIdentifier:@"Item1VC"]; NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[_detailViewController.navigationController viewControllers]]; [viewControllers removeLastObject];[viewControllers addObject:newVC]; [_detailViewController.navigationController setViewControllers:viewControllers animated:YES];}
Par expérience, vous allez devoir manipuler
viewControllers
directement la propriété de UINavigationController . Quelque chose comme ça devrait fonctionner:MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; [[self retain] autorelease]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; self.navigationController.viewControllers = controllers; [self.navigationController pushViewController:mevc animated: YES];
Remarque: j'ai changé la conservation / libération en une conservation / libération automatique, car c'est généralement plus robuste - si une exception se produit entre la conservation / la libération, vous ferez une fuite, mais la libération automatique s'en charge.
la source
Après beaucoup d'efforts (et peaufinant le code de Kevin), j'ai finalement compris comment faire cela dans le contrôleur de vue qui est sorti de la pile. Le problème que j'avais était que self.navigationController retournait nil après avoir supprimé le dernier objet du tableau des contrôleurs. Je pense que cela était dû à cette ligne dans la documentation de UIViewController sur la méthode d'instance navigationController "Renvoie un contrôleur de navigation uniquement si le contrôleur de vue est dans sa pile."
Je pense qu'une fois que le contrôleur de vue actuel est supprimé de la pile, sa méthode navigationController retournera nil.
Voici le code ajusté qui fonctionne:
UINavigationController *navController = self.navigationController; MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; navController.viewControllers = controllers; [navController pushViewController:mevc animated: YES];
la source
Merci, c'était exactement ce dont j'avais besoin. J'ai également mis ceci dans une animation pour obtenir la page curl:
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo]; UINavigationController *navController = self.navigationController; [[self retain] autorelease]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration: 0.7]; [UIView setAnimationTransition:<#UIViewAnimationTransitionCurlDown#> forView:navController.view cache:NO]; [navController popViewControllerAnimated:NO]; [navController pushViewController:mevc animated:NO]; [UIView commitAnimations];
La durée 0.6 est rapide, bonne pour 3GS et plus récent, 0.8 est encore un peu trop rapide pour la 3G.
Johan
la source
Si vous souhaitez afficher un autre contrôleur de vue par popToRootViewController, vous devez procéder comme suit:
UIViewController *newVC = [[WelcomeScreenVC alloc] initWithNibName:@"WelcomeScreenVC" bundle:[NSBundle mainBundle]]; NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]]; [viewControllers removeAllObjects]; [viewControllers addObject:newVC]; [[self navigationController] setViewControllers:viewControllers animated:NO];
Maintenant, toute votre pile précédente sera supprimée et une nouvelle pile sera créée avec votre rootViewController requis.
la source
J'ai dû faire une chose similaire récemment et j'ai basé ma solution sur la réponse de Michaels. Dans mon cas, j'ai dû supprimer deux contrôleurs de vue de la pile de navigation, puis ajouter un nouveau contrôleur de vue. Appel
deux fois, a bien fonctionné dans mon cas.UINavigationController *navController = self.navigationController; // retain ourselves so that the controller will still exist once it's popped off [[self retain] autorelease]; searchViewController = [[SearchViewController alloc] init]; NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease]; [controllers removeLastObject]; // In my case I want to go up two, then push one.. [controllers removeLastObject]; navController.viewControllers = controllers; NSLog(@"controllers: %@",controllers); controllers = nil; [navController pushViewController:searchViewController animated: NO];
la source
Cette
UINavigationController
méthode d'instance peut fonctionner ...Affiche les contrôleurs de vue jusqu'à ce que le contrôleur de vue spécifié soit le contrôleur de vue de dessus, puis met à jour l'affichage.
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
la source
Voici une autre approche qui ne nécessite pas de jouer directement avec le tableau viewControllers. Vérifiez si le contrôleur a déjà été ouvert, si c'est le cas, poussez-le.
TasksViewController *taskViewController = [[TasksViewController alloc] initWithNibName:nil bundle:nil]; if ([navigationController.viewControllers indexOfObject:taskViewController] == NSNotFound) { [navigationController pushViewController:taskViewController animated:animated]; } else { [navigationController popToViewController:taskViewController animated:animated]; }
la source
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy]; for(int i=0;i<controllers.count;i++){ [controllers removeLastObject]; } self.navigationController.viewControllers = controllers;
la source
Ma façon préférée de le faire est avec une catégorie sur UINavigationController. Les éléments suivants devraient fonctionner:
UINavigationController + Helpers.h #import
@interface UINavigationController (Helpers) - (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller; @end
UINavigationController + Helpers.m
#import "UINavigationController + Helpers.h"
@implementation UINavigationController (Helpers) - (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller { UIViewController* topController = self.viewControllers.lastObject; [[topController retain] autorelease]; UIViewController* poppedViewController = [self popViewControllerAnimated:NO]; [self pushViewController:controller animated:NO]; return poppedViewController; } @end
Ensuite, à partir de votre contrôleur de vue, vous pouvez remplacer la vue de dessus par une nouvelle par comme ceci:
[self.navigationController replaceTopViewControllerWithViewController: newController];
la source
Vous pouvez vérifier avec le tableau de contrôleurs de vue de navigation que vous vous donnez tous les contrôleurs de vue que vous avez ajoutés dans la pile de navigation. En utilisant ce tableau, vous pouvez revenir à un contrôleur de vue spécifique.
la source
Pour IOS monotouch / xamarin:
à l'intérieur de la classe UISplitViewController;
UINavigationController mainNav = this._navController; //List<UIViewController> controllers = mainNav.ViewControllers.ToList(); mainNav.ViewControllers = new UIViewController[] { }; mainNav.PushViewController(detail, true);//to have the animation
la source
Alternativement,
Vous pouvez utiliser
category
pour éviterself.navigationController
d'êtrenil
aprèspopViewControllerAnimated
il suffit d'appuyer et d'appuyer, c'est facile à comprendre, pas besoin d'accéder
viewControllers
...// UINavigationController+Helper.h @interface UINavigationController (Helper) - (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated; @end // UINavigationController+Helper.m @implementation UINavigationController (Helper) - (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated { UIViewController *v =[self popViewControllerAnimated:NO]; [self pushViewController:viewController animated:animated]; return v; } @end
Dans votre ViewController
// #import "UINavigationController+Helper.h" // invoke in your code UIViewController *v= [[MyNewViewController alloc] init]; [self.navigationController popThenPushViewController:v animated:YES]; RELEASE_SAFELY(v);
la source
Pas exactement la réponse mais peut être utile dans certains scénarios (le mien par exemple):
Si vous devez ouvrir le viewcontroller C et aller à B (hors de la pile) au lieu de A (celui ci-dessous C), il est possible de pousser B avant C et d'avoir les 3 sur la pile. En gardant la poussée B invisible et en choisissant de faire apparaître uniquement C ou C et B, vous pouvez obtenir le même effet.
problème initial A -> C (je veux faire apparaître C et montrer B, hors pile)
solution possible A -> B (poussé invisible) -> C (quand je pop C, je choisis d'afficher B ou aussi de le faire apparaître)
la source
J'utilise cette solution pour conserver l'animation.
[self.navigationController pushViewController:controller animated:YES]; NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers]; [newControllers removeObject:newControllers[newControllers.count - 2]]; [self.navigationController setViewControllers:newControllers];
la source