web-dev-qa-db-fra.com

Comment revenir au contrôleur de vue racine mais ensuite pousser vers une autre vue?

J'écris une application simple qui a 3 contrôleurs de vue. Le contrôleur de vue racine est un item listing, vue de base du tableau. Hors de ce contrôleur de vue, j'appuie sur deux contrôleurs de vue différents en fonction d'une interaction de l'utilisateur - un create item afficher le contrôleur ou un view item afficher le contrôleur.

Ainsi, les séquences de storyboard ressemblent juste à un V, ou quelque chose.

Sur mon create item contrôleur de vue, j'aimerais qu'il revienne au contrôleur de vue racine lorsque l'utilisateur crée un nouvel élément, mais ensuite, poussez sur view item contrôleur pour que je puisse regarder l'élément nouvellement créé.

Je n'arrive pas à faire fonctionner ça. Il est assez facile de revenir au contrôleur de vue racine, mais je ne peux pas pousser cela view item manette.

Des idées? J'ai collé mon code ci-dessous. La fonction pop fonctionne, mais la nouvelle vue n'apparaît jamais.

- (void) onSave:(id)sender {

    CLLocation *currentLocation = [[LocationHelper sharedInstance] currentLocation];

    // format the thread object dictionary
    NSArray* location = @[ @(currentLocation.coordinate.latitude), @(currentLocation.coordinate.longitude) ];
    NSDictionary* thread = @{ @"title": _titleField.text, @"text": _textField.text, @"author": @"mustached-bear", @"location": location };

    // send the new thread to the api server
    [[DerpHipsterAPIClient sharedClient] postPath:@"/api/thread"
                                       parameters:thread
                                          success:^(AFHTTPRequestOperation *operation, id responseObject) {

                                              // init thread object
                                              Thread *thread = [[Thread alloc] initWithDictionary:responseObject];

                                              // init view thread controller
                                              ThreadViewController *viewThreadController = [[ThreadViewController alloc] init];
                                              viewThreadController.thread = thread;

                                              [self.navigationController popToRootViewControllerAnimated:NO];
                                              [self.navigationController pushViewController:viewThreadController animated:YES];

                                          }
                                          failure:^(AFHTTPRequestOperation *operation, NSError *error) {

                                              [self.navigationController popToRootViewControllerAnimated:YES];

                                          }];

}
16
Ryan

Un moyen facile d'accomplir ce que vous voulez faire est de construire une logique simple dans vos principaux contrôleurs de vue racine - (void) méthode viewWillAppear et d'utiliser un rappel délégué pour actionner le commutateur logique. essentiellement une "référence arrière" au contrôleur racine. voici un petit exemple.

contrôleur racine principal (considérez ce contrôleur comme un) - appelez-le bien un contrôleur

@property (nonatomic) BOOL jumpNeeded;

configurer une logique dans

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    self.jumpNeeded ? NSLog(@"jump needed") : NSLog(@"no jump needed");

    if (self.jumpNeeded) {
        NSLog(@"jumping");
        self.jumpNeeded = NO;
        [self performSegueWithIdentifier:@"controllerC" sender:self];
    }   
}

Maintenant, dans votre contrôleur racine principal, lorsqu'une ligne tableview est sélectionnée, faites quelque chose comme ceci lorsque vous poussez vers controllerB dans votre tableView a sélectionné la méthode

[self performSegueWithIdentifer@"controllerB" sender:self];

puis implémentez votre méthode de préparation à la séquence

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

  //setup controller B
  if([segue.identifier isEqualTo:@"controllerB"]){
    ControllerB *b = segue.destinationViewController;
    b.delegate = self;  //note this is the back reference
  }

  //implement controller c here if needed
}

Passez maintenant à controllerB, vous devez définir une propriété appelée "delegate" pour contenir la référence arrière et vous devez importer le fichier d'en-tête à partir du contrôleur racine

#import "controllerA"

@property (nonatomic,weak) controllerA *delegate;

puis juste avant de revenir au contrôleur A, vous définissez le drapeau

   self.delegate.jumpNeeded = YES;
    [self.navigationController popViewControllerAnimated:YES];

et c'est à peu près tout. Vous n'avez rien à faire avec controllerC. Il y a quelques autres façons de faire, mais c'est assez simple pour vos besoins. J'espère que cela marchera pour toi.

6
CocoaEv

Si je vous comprends bien, vous avez une pile de contrôleurs de vue:

A (root) - B - C - D - E

Et vous voulez qu'il devienne:

A (root) - F

Droite? Dans ce cas:

NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray array];

// preserve the root view controller
[newViewControllers addObject:[viewControllers objectAtIndex:0]];
// add the new view controller
[newViewControllers addObject:viewThreadController];
// animatedly change the navigation stack
[self.navigationController setViewControllers:newViewControllers animated:YES];

Swift 4

// get current view controllers in stack and replace them
let viewControllers = self.navigationController!.viewControllers
let newViewControllers = NSMutableArray()

// preserve the root view controller
newViewControllers.add(viewControllers[0])
// add the new view controller
newViewControllers.add(viewThreadController)
// animatedly change the navigation stack
self.navigationController?.setViewControllers(newViewControllers as! [UIViewController], animated: true)
40
Dave DeLong

Je pense
[self.navigationController pushViewController:viewThreadController animated:YES]; utilise un NavigationController différent de l'instruction précédente. Parce qu'après avoir sauté dans le contrôleur de vue racine, vous perdez le contrôleur de navigation dans lequel vous vous trouvez. Résolvez cela en utilisant ce code à la place

UINavigationController *nav = self.navigationController; 
[self.navigationController popToRootViewControllerAnimated:NO];
[nav pushViewController:viewThreadController animated:YES];

Je pense aussi que cela ne résoudra pas tout votre problème. Vous obtiendrez probablement une erreur indiquant que deux sauts et poussées rapides peuvent invalider le NavigationController.
Et pour résoudre ce problème, vous pouvez soit pousser le NavigationController dans la méthode viewDidDissappear du 2nd View Controller, soit le pousser dans la méthode viewDidAppear dans le Main View Controller (liste des éléments).

8
M.C.

Partager une catégorie sur UINavigationController basée sur la réponse de Dave DeLong que nous utilisons dans notre application pour que le bouton de retour fonctionne toujours comme requis.

@implementation UINavigationController (PushNewAndClearNavigationStackToParent)

- (void) pushNewControllerAndClearStackToParent:(UIViewController*)newCont animated:(BOOL)animated
{
    NSArray *viewControllers = self.viewControllers;
    NSMutableArray *newViewControllers = [NSMutableArray array];
    
    // preserve the root view controller
    [newViewControllers addObject:[viewControllers firstObject]];
    // add the new view controller
    [newViewControllers addObject:newCont];
    // animatedly change the navigation stack
    [self setViewControllers:newViewControllers animated:animated];
}

@end
5
jlichti
NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray array];

// preserve the root view controller
for(int i = 0; i < [viewControllers count];i++){
    if(i != [viewControllers count]-1)
        [newViewControllers addObject:[viewControllers objectAtIndex:i]];
}
// add the new view controller
[newViewControllers addObject:conversationViewController];
// animatedly change the navigation stack
[self.navigationController setViewControllers:newViewControllers animated:YES];
1
user5018497

J'ai utilisé la réponse de Dave comme source d'inspiration et l'ai fait pour Swift:

let newViewControllers = NSMutableArray()
newViewControllers.addObject(HomeViewController())
newViewControllers.addObject(GroupViewController())
let swiftArray = NSArray(array:newViewControllers) as! Array<UIViewController>
self.navigationController?.setViewControllers(swiftArray, animated: true)
  1. Remplacez HomeViewController () par ce que vous voulez que votre contrôleur de vue de dessous soit
  2. Remplacez GroupViewController () par ce que vous voulez que votre contrôleur de vue de dessus soit
0
Rutger Huijsmans

Si ce que vous voulez faire est de naviguer de quelque manière que ce soit à l'intérieur du même contrôleur de navigation ... vous pouvez accéder à la navigationStack A.K.A. viewControllers, puis définissez les viewcontrollers dans l'ordre que vous souhaitez, ou ajoutez ou supprimez certains ... C'est la manière la plus propre et native de le faire.

        UINavigationController* theNav = viewControllerToDismiss.navigationController;
        UIViewController* theNewController = [UIViewController new];
        UIViewController* rootView = theNav.viewControllers[0];
        [theNav setViewControllers:@[
                                     rootView,
                                     theNewController
                                     ] animated:YES];

faites les validations nécessaires pour ne pas avoir d'exception.

0
user2387149

Je ne pense pas que cela soit possible car le retour en arrière va tout désallouer.

Je pense qu'une meilleure façon est de mettre vos données sur une classe singleton modèle, de passer au rootviewcontroller et d'écouter la fin de la pop. Ensuite, vous vérifiez s'il y a des données stockées sur le modèle afin de savoir si vous devez pousser un nouveau viewcontroller.

0
polyclick