Comment afficher deux vues à la fois à partir d'un contrôleur de navigation?

90

Je veux revenir à la troisième vue de la pile de navigation et revenir à la première vue.

Je sais comment afficher une vue à la fois:

[self.navigationController popViewControllerAnimated:YES];

Mais comment faire deux à la fois?

Adam Waite
la source
2
Meta comment: @lubilis répond tout en bas est le meilleur. Les réponses les mieux notées étaient bonnes en leur temps mais ne sont plus pertinentes.
n13

Réponses:

132

Vous pouvez également essayer ceci pour sauter entre la pile du contrôleur de navigation

NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
for (UIViewController *aViewController in allViewControllers) {
    if ([aViewController isKindOfClass:[RequiredViewController class]]) {
        [self.navigationController popToViewController:aViewController animated:NO];
    }
}
Rencontrer
la source
6
imo c'est de loin la meilleure méthode, mais vous devriez référencer votre contrôleur d'uinavigation en utilisant self.navigationcontroller
henghonglee
2
Je suis d'accord, c'est la meilleure solution si l'utilisateur souhaite ramener la pile vers un certain contrôleur de vue. Disons que vous ne savez pas quel viewcontroller c'est, vous pouvez toujours implémenter un système dans lequel vous spécifieriez combien de viewcontrollers vous souhaitez sortir de la pile et obtenir le viewcontroller cible du tableau allViewControllers avec objectAtIndex: (allViewControllers.count - 1 - montant). -1 car les tableaux sont bien entendu basés sur zéro.
Jovan
1
J'adore cette solution. Exactement ce que je cherchais
Designer023
3
Fonctionne également lors de l'itération sur le tableau d'origine du navigateur-> viewControllers (pas besoin de le convertir en un tableau mutable)
Arik Segal
REMARQUE! Cela va itérer la pile du début -> à la fin, pour être exactement correct, vous devez inverser la pile. Parce que si vous avez plusieurs VC du même type, vous supprimerez le mauvais VC dans le mauvais ordre dans la pile.
StackUnderflow
71

Voici deux UINavigationControllerextensions qui peuvent résoudre votre problème. Je recommanderais d'utiliser le premier qui apparaît dans une UIViewControllerclasse spécifique:

extension UINavigationController {

  func popToViewController(ofClass: AnyClass, animated: Bool = true) {
    if let vc = viewControllers.filter({$0.isKind(of: ofClass)}).last {
      popToViewController(vc, animated: animated)
    }
  }

  func popViewControllers(viewsToPop: Int, animated: Bool = true) {
    if viewControllers.count > viewsToPop {
      let vc = viewControllers[viewControllers.count - viewsToPop - 1]
      popToViewController(vc, animated: animated)
    }
  }

}

et utilisez-le comme ceci:

// pop to SomeViewController class
navigationController?.popToViewController(ofClass: SomeViewController.self)

// pop 2 view controllers
navigationController?.popViewControllers(viewsToPop: 2)
budidino
la source
3
Pour Swift (option 1), vous pouvez remplacer les deux removeLastpar juste removeLast(2).
Dominic K
Qu'en est-il des méthodes d'appel du cycle de vie des contrôleurs? DidAppear and ect
Mike Glukhov
1
(Swift 4) Il vous manque des crochets dans cette ligne let vc = viewControllers[viewControllers.count - viewsToPop + 1], afin de fonctionner correctement, vous devez le remplacer par: let vc = viewControllers[viewControllers.count - (viewsToPop + 1)]oulet vc = viewControllers[viewControllers.count - viewsToPop - 1]
MMiroslav
@MMiroslav vous avez raison. J'ai mis à jour ma réponse. Merci / Hvala;)
budidino
1
Cette réponse a été mise à jour après avoir publié ma réponse ci-dessous. Essentiellement une copie de mon code ^ _ ^
lubilis
44

Vous pouvez accéder au contrôleur de vue «racine» (premier) avec popToRootViewControllerAnimated:

[self.navigationController popToRootViewControllerAnimated:YES];

// If you want to know what view controllers were popd:
// NSArray *popdViewControllers = [self.navigationController popToRootViewControllerAnimated:YES];

UINavigationControllerRéférence :

Ouvre tous les contrôleurs de vue de la pile à l'exception du contrôleur de vue racine et met à jour l'affichage.

Valeur de retour
Un tableau de contrôleurs de vue qui sont extraits de la pile.

chown
la source
1
ha, je ne peux pas croire que j'ai passé si longtemps à chercher une réponse aussi simple, merci!
Adam Waite
1
Swift 3: self.navigationController? .PopToRootViewController (animé: vrai);
dianakarenms
C'est précisément ce que je cherchais!
Canucklesandwich du
29
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];   

objectAtIndex: 1 -> vous pouvez passer l'index auquel vous souhaitez accéder

Zaraki
la source
Merci pour le tour, j'étais sur le point de le faire dans le mauvais sens.
Linus
18

Swift 2 - xCode 7.3

Cela pourrait être une extension très utile pour pop UIViewControllers:

extension UINavigationController {

    func popToViewControllerOfType(classForCoder: AnyClass) {
        for controller in viewControllers {
            if controller.classForCoder == classForCoder {
                popToViewController(controller, animated: true)
                break
            }
        }
    }

    func popViewControllers(controllersToPop: Int, animated: Bool) {
        if viewControllers.count > controllersToPop {
            popToViewController(viewControllers[viewControllers.count - (controllersToPop + 1)], animated: animated)
        } else {
            print("Trying to pop \(controllersToPop) view controllers but navigation controller contains only \(viewControllers.count) controllers in stack")
        }
    }
}
lubilis
la source
2
Cela nécessite plus de vote positif ... J'étais sur le point d'écrire cette extension. Je vais juste utiliser le vôtre merci;)
n13
1
@ n13 pourquoi est-ce mieux que la réponse de budidino?
Crashalot le
1
L'utilisation d'une extension permet un code beaucoup plus propre. Vous pouvez prendre la réponse de budidino et en faire une extension, mais celle-ci vous évite l'effort. De plus, cette réponse vérifie les cas d'erreur et gère les erreurs avec élégance (par exemple, en essayant de faire apparaître plus que ce que vous avez)
n13
1
J'aime cette réponse. M'a fait repenser et mettre à jour la réponse que j'ai publiée. J'ai fait apparaître mon code à la dernière instance de la classe recherchée dans le tableau viewControllers car c'est probablement le comportement souhaité.
budidino
15

Si vous voulez juste afficher 2 à la fois parce que votre rootViewController est (beaucoup) `` plus profond '', alors 2 vous pouvez envisager d'ajouter une catégorie à UIviewController par exemple:

UINavigationController + popTwice.h

#import <UIKit/UIKit.h>
@interface UINavigationController (popTwice)

- (void) popTwoViewControllersAnimated:(BOOL)animated;

@end

UINavigationController + popTwice.m

#import "UINavigationController+popTwice.h"

@implementation UINavigationController (popTwice)

- (void) popTwoViewControllersAnimated:(BOOL)animated{
    [self popViewControllerAnimated:NO];
    [self popViewControllerAnimated:animated];
}

@end

Importez la catégorie #import "UINavigationController+popTwice.h"quelque part dans votre implémentation et utilisez cette ligne de code pour afficher 2 contrôleurs à la fois:

[self.navigationController popTwoViewControllersAnimated:YES];

n'est-ce pas génial? :)

Attache-moi
la source
6
et si vous avez besoin de faire apparaître trois vues, vous écrirez "UINavigationController + popThrice.m" ??????
Rendez-vous
8
vous pouvez simplement passer un paramètre pour le nombre de viewControllers à pop, et envelopper [self popViewControllerAnimated: NO]; dans une boucle for, de count-1.
noRema
Ce n'est pas la bonne façon, si vous voulez pot à 2,3, ... n'importe quel contrôleur l'identifie par boucle et utilise ensuite [self.navigationController popToViewControllerAnimated: YES] ;. Ceci est mentionné ci-dessus est un très mauvais codage et peut montrer une interface utilisateur scintillante et une mauvaise expérience utilisateur.
Vicky Dhas
10

Swift 4:

func popViewControllerss(popViews: Int, animated: Bool = true) {
    if self.navigationController!.viewControllers.count > popViews
    {
        let vc = self.navigationController!.viewControllers[self.navigationController!.viewControllers.count - popViews - 1]
         self.navigationController?.popToViewController(vc, animated: animated)
    }
}

Ensuite, utilisez cette méthode

self.popViewControllerss(popViews: 2)
Saifan Nadaf
la source
Bon, ce que je cherchais. Merci.
maddysan
6

Vous pouvez également essayer celui-ci: -

[self.navigationController popToViewController:yourViewController animated:YES];
Leena
la source
6
//viewIndex = 1;
//viewIndex = 2;
//viewIndex = 3;

// **[viewControllers objectAtIndex: *put here your viewindex number*]

NSArray *viewControllers = [self.navigationController viewControllers];
[self.navigationController popToViewController:[viewControllers objectAtIndex:viewIndex] animated:NO];
Sabareesh
la source
4
    NSMutableArray *newStack = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
    [newStack removeLastObject];
    [newStack removeLastObject];
    [self.navigationController setViewControllers:newStack animated:YES];
patrick-fitzgerald
la source
4

Dans Swift 3 , vous pouvez afficher un, deux ou plusieurs contrôleurs de vue à partir du contrôleur de navigation comme ça

let viewControllers = self.navigationController!.viewControllers as [UIViewController]
    for aViewController:UIViewController in viewControllers {
        if aViewController.isKind(of: FromWhereYouWantToGoController.self) {
            _ = self.navigationController?.popToViewController(aViewController, animated: true)
        }
    }

Ici FromWhereYouWantToGoController est le contrôleur de destination. J'espère que ça aide.

Riajur Rahman
la source
3

Vous pouvez passer le contrôleur de vue initial (celui sur lequel vous voulez revenir) puis appeler cette ligne dans la dernière vue:

[self.navigationController popToViewController:yourInitialViewController animated:YES];

L.

Lucas
la source
3

Je n'ai pas vu cette réponse ici. Si vous ne voulez en afficher que quelques-uns (pas jusqu'à la racine), vous pouvez simplement parcourir self.navigationController.viewControllers en vérifiant les types de classe, ou si vous avez une référence, utilisez cela:

for (UIViewController *aViewController in self.navigationController.viewControllers) {
   if ([aViewController isKindOfClass:[SMThumbnailViewController class]]) {
      [self.navigationController popToViewController:aViewController animated:YES];
   }
}
VaporwareWolf
la source
2

vous pouvez revenir au contrôleur de vue racine

[self.navigationController popToRootViewControllerAnimated:YES];

ou, si la vue que vous souhaitez afficher n'est pas la première, vous devrez à nouveau apparaître dans la vue de votre vue précédente.

Robotnik
la source
2

Voici une petite fonction que j'utilise pour revenir en arrière sur X ViewControllers:

-(void)backMultiple:(id)data {
    int back = 2; //Default to go back 2 
    int count = [self.navigationController.viewControllers count];

    if(data[@"count"]) back = [data[@"count"] intValue];

    //If we want to go back more than those that actually exist, just go to the root
    if(back+1 > count) {
        [self.navigationController popToRootViewControllerAnimated:YES];
    }
    //Otherwise go back X ViewControllers 
    else {
        [self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:count-(back+1)] animated:YES];
    }
}
marque
la source
2

Version Swift à partir de (Swift 1.2 / Xcode 6.3.1) de popping deux fois ou plus

 var viewControllers = self.navigationController?.viewControllers
 viewControllers?.removeLast()
 viewControllers?.removeLast()
 self.navigationController?.setViewControllers(viewControllers, animated: true)

ou

 var viewControllers = self.navigationController?.viewControllers
 var viewsToPop = 2
 var end = (viewControllers?.count)!
 var start = end - viewsToPop
 viewControllers?.removeRange(Range<Int>(start: start, end: end))
 self.navigationController?.setViewControllers(viewControllers, animated: true)
Glenn S
la source
1

Vous pouvez utiliser la pile des UIViewControllers. 1. Récupérez le tableau de tous les viewControllers de la pile. 2. Parcourez tout le tableau et trouvez le viewController souhaité en faisant
correspondre le type de classe. 3. Ouvrez le viewController.

func popToSpecificViewC
{
let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController];
        for viewController:UIViewController in viewControllers
        {
            if viewController.isKind(of: WelcomeViewC.self)
            {
                _ = self.navigationController?.popToViewController(viewController, animated: true)
            }
        }
}
Peeyush Karnwal
la source
0

En utilisant une simple extension UINavigationController :

extension UINavigationController {
    func popViewControllers(_ count: Int) {
        guard viewControllers.count > count else { return }
        popToViewController(viewControllers[viewControllers.count - count - 1], animated: true)
    }
}
Balázs Vincze
la source