Question

J'ai une longue vue hiérarchique des contrôleurs;

dans le premier View Controller J'utilise ce code:

SecondViewController *svc = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
[self presentModalViewController:svc animated:YES];    
[svc release];

Dans le second View Controller J'utilise ce code:

ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self presentModalViewController:tvc animated:YES];    
[tvc release];

et ainsi de suite.

Alors il y a un moment où j'ai beaucoup de contrôleurs de vue et je dois revenir au premier View Controller. Si je reviens un pas à la fois, je l'utilise dans chaque View Controller ce code:

[self dismissModalViewControllerAnimated:YES];

Si je veux revenir directement à partir de la, disons, sixième View Controller à la première, ce que je dois faire pour rejeter tous les contrôleurs à la fois?

Merci

Était-ce utile?

La solution 2

Je trouve la solution.

Bien sûr, vous pouvez trouver la solution la plus évidente lieu si la lecture de la référence UIViewController pour la méthode dismissModalViewControllerAnimated ...

  

Si vous présentez plusieurs vue modale   les contrôleurs de suite, et ainsi   construire une pile de vue modal   contrôleurs, l'appel de cette méthode sur un   vue contrôleur inférieur dans la pile   congédie son point de vue enfant immédiat   contrôleur et tous les contrôleurs de vue   au-dessus de cet enfant sur la pile. Quand   Dans ce cas, seul le plus en haut vue   est rejeté de façon animée;   les contrôleurs de vue intermédiaires sont   simplement retirée de la pile. le   de plus haut point de vue est rejeté en utilisant son   style de transition modale, ce qui peut   différer des styles utilisés par d'autres   afficher les contrôleurs plus bas dans la pile.

il suffit d'appeler la dismissModalViewControllerAnimated sur la vue cible. J'ai utilisé le code suivant:

[[[[[self parentViewController] parentViewController] parentViewController] parentViewController] dismissModalViewControllerAnimated:YES];

pour revenir à ma maison.

Autres conseils

Oui. il y a déjà un tas de réponses, mais je vais juste ajouter un à la fin de la liste de toute façon. Le problème est que nous avons besoin d'obtenir une référence au contrôleur de vue sur la base de la hiérarchie. Comme dans la réponse de @Juan Munhoes junior, vous pouvez marcher la hiérarchie, mais il peut y avoir différentes voies que l'utilisateur pourrait prendre, si c'est une réponse assez fragile. Il est difficile de ne pas étendre cette solution simple, mais simplement marcher la hiérarchie à la recherche du bas de la pile. L'appel d'un rejet en bas obtiendra tous les autres, aussi.

-(void)dismissModalStack {
    UIViewController *vc = self.presentingViewController;
    while (vc.presentingViewController) {
        vc = vc.presentingViewController;
    }
    [vc dismissViewControllerAnimated:YES completion:NULL];
}

est simple et flexible: si vous voulez chercher un type particulier de contrôleur de vue dans la pile, vous pouvez ajouter une logique basée sur [vc isKindOfClass:[DesiredViewControllerClass class]].

iOS 8+ méthode universelle pour licenciement sans contexte fullscreen mal d'animation. Dans Objective-C et Swift

Objective-C

- (void)dismissModalStackAnimated:(bool)animated completion:(void (^)(void))completion {
    UIView *fullscreenSnapshot = [[UIApplication sharedApplication].delegate.window snapshotViewAfterScreenUpdates:false];
    [self.presentedViewController.view insertSubview:fullscreenSnapshot atIndex:NSIntegerMax];
    [self dismissViewControllerAnimated:animated completion:completion];
}

Swift

func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
    if let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false) {
        presentedViewController?.view.addSubview(fullscreenSnapshot)
    }
    if !isBeingDismissed {
        dismiss(animated: animated, completion: completion)
    }
}

tl; dr

Qu'est-ce qui ne va pas avec d'autres solutions?

Il existe de nombreuses solutions, mais aucun d'entre eux comptent dans le mauvais contexte licencie donc:

par exemple. racine A -> B Presents -> C Présente et que vous voulez rejeter à l'A de C, vous pouvez en appelant officielement dismissViewControllerAnimated sur rootViewController.

 [[UIApplication sharedApplication].delegate.window.rootViewController dismissModalStackAnimated:true completion:nil];

Cependant appel à rejeter cette racine de C conduira à un bon comportement avec mauvaise transition (B à A aurait été vu au lieu de C à A).


si

Je créé la méthode rejeter universelle. Cette méthode capturer des images de plein écran actuel et placez-le sur le contrôleur de vue présenté par le récepteur, puis rejeter tout (Exemple: default appelé rejet de C, mais B est vraiment considéré comme rejeté).

Supposons que votre premier contrôleur de vue est aussi la racine / Initial Controller View (celui que vous Nominé dans votre Storyboard comme contrôleur Affichage initial). Vous pouvez le configurer pour écouter les demandes de rejeter tous ses contrôleurs de vue présentés:

FirstViewController:

- (void)viewDidLoad {
    [super viewDidLoad];

    // listen to any requests to dismiss all stacked view controllers
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dismissAllViewControllers:) name:@"YourDismissAllViewControllersIdentifier" object:nil];

    // the remainder of viewDidLoad ...
}

// this method gets called whenever a notification is posted to dismiss all view controllers
- (void)dismissAllViewControllers:(NSNotification *)notification {
    // dismiss all view controllers in the navigation stack
    [self dismissViewControllerAnimated:YES completion:^{}];
}

Et dans un autre contrôleur de vue sur la pile de navigation qui décide que nous devrions revenir au sommet de la pile de navigation:

[[NSNotificationCenter defaultCenter] postNotificationName:@"YourDismissAllViewControllersIdentifier" object:self];

Cela devrait rejeter tous les contrôleurs de vue présentés avec une animation de façon modale, ne laissant que le contrôleur de vue racine. Cela fonctionne également si votre contrôleur de vue initial est un UINavigationController et le premier contrôleur de vue est défini comme son contrôleur de vue racine.

Astuce Bonus: Il est important que le nom de notification est identique. Probablement une bonne idée de définir ce nom quelque part de notification dans l'application comme une variable, pour ne pas obtenir une mauvaise communication en raison d'erreurs de frappe.

[[self presentingViewController]presentingViewController]dismissModalViewControllerAnimated:NO];

Vous pouvez également mettre en œuvre un délégué dans tous les contrôleurs que vous voulez rejeter

Si vous utilisez tous sont contrôleur de vue modèle que vous pouvez utiliser la notification pour rejeter tout contrôleur de vue preseted.

1.Register Notification RootViewController comme ceci

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(dismissModelViewController)
                                             name:dismissModelViewController
                                           object:nil];

2.Mettre en œuvre la fonction de dismissModelViewController dans RootViewController

- (void)dismissModelViewController
{
    While (![self.navigationController.visibleViewController isMemberOfClass:[RootviewController class]])
    {
        [self.navigationController.visibleViewController dismissViewControllerAnimated:NO completion:nil];
    }
}

3.Avis afficher tous les proches ou événement bouton rejeter.

   [[NSNotificationCenter defaultCenter] postNotificationName:dismissModelViewController object:self];

Swift:

self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)

Le problème avec la plupart des solutions est que lorsque vous remercierez la pile de viewControllers présentées, l'utilisateur brièvement voir le premier présenté viewController dans la pile comme il est rejeté. Une excellente solution de Jakub qui permet de résoudre. Voici une extension en fonction de sa réponse.

extension UIViewController {

    func dismissAll(animated: Bool, completion: (() -> Void)? = nil) {
        if let optionalWindow = UIApplication.shared.delegate?.window, let window = optionalWindow, let rootViewController = window.rootViewController, let presentedViewController = rootViewController.presentedViewController  {
            if let snapshotView = window.snapshotView(afterScreenUpdates: false) {
                presentedViewController.view.addSubview(snapshotView)
                presentedViewController.modalTransitionStyle = .coverVertical
            }
            if !isBeingDismissed {
                rootViewController.dismiss(animated: animated, completion: completion)
            }
        }
    }

}

Utilisation:. Appelez cette fonction d'extension de toute viewController présentée que vous souhaitez rejeter revenir à la racine

@IBAction func close() {
    dismissAll(animated: true)
}

Essayez ceci ..

ThirdViewController *tvc = [[ThirdViewController alloc] initWithNibName:@"ThirdViewController" bundle:nil];
[self.view addsubview:tvc];    
[tvc release];

Tout d'abord merci Oscar Peli pour votre code.

Pour commencer votre NavigationController au début, vous pourriez faire un peu plus de cette façon dynamique. (Au cas où vous ne connaissez pas le nombre de ViewControllers dans la pile)

NSArray *viewControllers = self.navigationController.viewControllers;
[self.navigationController popToViewController: [viewControllers objectAtIndex:0] animated: YES];
  id vc = [self presentingViewController];
  id lastVC = self;
  while (vc != nil) {
    id tmp = vc;
    vc = [vc presentingViewController];
    lastVC = tmp;
  }
  [lastVC dismissViewControllerAnimated:YES completion:^{
}];

Voici une solution que j'utilise pour pop et révoquer tous les contrôleurs de vue afin de revenir au contrôleur de vue racine. J'ai ces deux méthodes dans une catégorie de UIViewController:

+ (UIViewController*)topmostViewController
{
    UIViewController* vc = [[[UIApplication sharedApplication] keyWindow] rootViewController];
    while(vc.presentedViewController) {
        vc = vc.presentedViewController;
    }
    return vc;
}

+ (void)returnToRootViewController
{
    UIViewController* vc = [UIViewController topmostViewController];
    while (vc) {
        if([vc isKindOfClass:[UINavigationController class]]) {
            [(UINavigationController*)vc popToRootViewControllerAnimated:NO];
        }
        if(vc.presentingViewController) {
            [vc dismissViewControllerAnimated:NO completion:^{}];
        }
        vc = vc.presentingViewController;
    }
}

Je viens d'appeler

[UIViewController returnToRootViewController];

Une version rapide avec quelques ajouts sur la base ce commentaire

func dismissModalStack(viewController: UIViewController, animated: Bool, completionBlock: BasicBlock?) {
    if viewController.presentingViewController != nil {
        var vc = viewController.presentingViewController!
        while (vc.presentingViewController != nil) {
            vc = vc.presentingViewController!;
        }
        vc.dismissViewControllerAnimated(animated, completion: nil)

        if let c = completionBlock {
            c()
        }
    }
}

récursive plus simple:

extension UIViewController {
    final public func dismissEntireStackAndSelf(animate: Bool = true) {
        // Always false on non-calling controller
        presentedViewController?.ip_dismissEntireStackAndSelf(false)
        self.dismissViewControllerAnimated(animate, completion: nil)
    }
}

Cela forcera près tous les contrôleurs de l'enfant et seulement Animer auto. Vous pouvez passer pour ce que vous voulez, mais si vous animez chaque contrôleur ils vont un par un et il est lent.

Appeler

baseController.dismissEntireStackAndSelf()

Swift 3 l'extension sur la base des réponses ci-dessus.

Principe pour une pile comme ce qui suit: A -> B -> C -> D

  • Prendre un instantané de D
  • Ajouter cet instantané sur B
  • Rejeter de B sans animation
  • A la fin, de A à rejeter l'animation

    extension UIViewController {
    
        func dismissModalStack(animated: Bool, completion: (() -> Void)?) {
            let fullscreenSnapshot = UIApplication.shared.delegate?.window??.snapshotView(afterScreenUpdates: false)
            if !isBeingDismissed {
                var rootVc = presentingViewController
                while rootVc?.presentingViewController != nil {
                    rootVc = rootVc?.presentingViewController
                }
                let secondToLastVc = rootVc?.presentedViewController
                if fullscreenSnapshot != nil {
                    secondToLastVc?.view.addSubview(fullscreenSnapshot!)
                }
                secondToLastVc?.dismiss(animated: false, completion: {
                    rootVc?.dismiss(animated: true, completion: completion)
                })
            }
        }
    }
    

Un peu vacillante sur simulateur, mais pas sur l'appareil.

une extension rapide basée sur les réponses ci-dessus:

extension UIViewController {

    func dismissUntilAnimated<T: UIViewController>(animated: Bool, viewController: T.Type, completion: ((viewController: T) -> Void)?) {
        var vc = presentingViewController!
        while let new = vc.presentingViewController where !(new is T) {
            vc = new
        }
        vc.dismissViewControllerAnimated(animated, completion: {
            completion?(viewController: vc as! T)
        })
    }
}

Version 3.0 Swift:

extension UIViewController {

    /// Dismiss all modally presented view controllers until a specified view controller is reached. If no view controller is found, this function will do nothing.

    /// - Parameter reached:      The type of the view controller to dismiss until.
    /// - Parameter flag:         Pass `true` to animate the transition.
    /// - Parameter completion:   The block to execute after the view controller is dismissed. This block contains the instance of the `presentingViewController`. You may specify `nil` for this parameter.
    func dismiss<T: UIViewController>(until reached: T.Type, animated flag: Bool, completion: ((T) -> Void)? = nil) {
        guard let presenting = presentingViewController as? T else {
            return presentingViewController?.dismiss(until: reached, animated: flag, completion: completion) ?? ()
        }

        presenting.dismiss(animated: flag) {
            completion?(presenting)
        }
    }
}

Complètement oublié pourquoi je fait cela comme il est logique incroyablement stupide compte tenu de la plupart du temps du contrôleur de vue présentant d'un contrôleur de vue modal est UITabBarController rendu cela complètement inutile. Il est beaucoup plus logique d'acquérir réellement l'instance du contrôleur de vue de base et dismiss d'appel à ce sujet.

Pour Swift 3.0 +

self.view.window!.rootViewController?.dismissViewControllerAnimated(false, completion: nil)
  

rejettera tous les contrôleurs de vue présentés sur votre   RootViewController.

Utilisez cette solution générique pour résoudre ce problème:

- (UIViewController*)topViewController
{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }
    return topController;
}


- (void)dismissAllModalController{

    __block UIViewController *topController = [self topViewController];

    while (topController.presentingViewController) {
        [topController dismissViewControllerAnimated:NO completion:^{

        }];
        topController = [self topViewController];
    }
}

Rejeter le haut VC animé et les autres non. Si vous hace trois modal VC

[self dismissModalViewControllerAnimated:NO]; // First
[self dismissModalViewControllerAnimated:NO]; // Second
[self dismissModalViewControllerAnimated:YES]; // Third

EDIT:. Si vous voulez faire cela avec une seule méthode, vous sauver la hiérarchie dans un tableau de VC et de rejeter le dernier objet animé et les autres pas

Document d'Apple sur le rejeter (animation: achèvement :) méthode .

Dans la section Discussion, il a dit:

any intermediate view controllers are simply removed from the stack.
  

Si vous présentez plusieurs contrôleurs de vue successivement, créant ainsi une pile de contrôleurs de vue présentés, appelant cette méthode sur un contrôleur de vue bas dans la pile rejette son contrôleur immédiat de vue des enfants et tous les contrôleurs de vue ci-dessus cet enfant sur la pile. Lorsque cela se produit, seul le haut la plus vue est rejeté de façon animée; les contrôleurs de vue intermédiaires sont simplement retirés de la pile. Le plus en haut vue est rejeté à l'aide de son style de transition modal, qui peuvent différer des modèles utilisés par d'autres contrôleurs de vue plus bas dans la pile.

En d'autres termes, si la pile de contrôleur de vue comme suit

Root -> A -> B -> C -> D ... -> Z

D appelle méthode de dismiss, tous les contrôleurs vue behide D, ex:. (E ... Z), sera retirée de la pile

rapide 4 Xcode 9 Cela vous aide.

var vc : UIViewController = self.presentingViewController!
        while ((vc.presentingViewController) != nil) {
            vc = vc.presentingViewController!
        }
        vc.dismiss(animated: true, completion: nil)

Profitez !!! :)

Si vous retournez à droite au début, vous pouvez utiliser le code     [Self.navigationController popToRootViewControllerAnimated: OUI];

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top