Domanda

Ho una gerarchia View Controller lungo;

nel primo View Controller io uso questo codice:

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

Nel secondo View Controller io uso questo codice:

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

e così via.

Quindi non v'è un momento in cui ho molti View Controller e ho bisogno di tornare alla prima View Controller. Se torno un passo alla volta, io uso in ogni controller Visualizza questo codice:

[self dismissModalViewControllerAnimated:YES];

Se voglio tornare direttamente dal, diciamo, sesto View Controller al primo, quello che devo fare per respingere tutti i controller in una volta?

Grazie

È stato utile?

Soluzione 2

ho trovato la soluzione.

Naturalmente si può trovare la soluzione nel posto più ovvio così la lettura dal riferimento UIViewController per il metodo dismissModalViewControllerAnimated ...

  

Se si presentano diversi vista modale   controllori in successione, e quindi   costruire una pila di vista modale   controllori, chiamata a questo metodo su un   guarda controllore abbassare nella pila   respinge la sua visione immediata bambino   controller e tutti controller di vista   sopra quel bambino in pila. quando   In questo caso, solo il più in alto vista   è respinto in modo animato;   eventuali controller di vista intermedi   semplicemente rimossa dalla pila. Il   più in alto vista è respinto utilizzando il suo   stile di transizione modale, che può   differire dai stili utilizzati da altri   Vista controllori abbassano nella pila.

in modo che sia abbastanza per chiamare il dismissModalViewControllerAnimated sul bersaglio View. Ho usato il seguente codice:

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

per tornare a casa mia.

Altri suggerimenti

Sì. ci sono già un sacco di risposte, ma sto solo andando a aggiungere uno al fine della lista in ogni caso. Il problema è che abbiamo bisogno di ottenere un riferimento al controller della vista alla base della gerarchia. Come nella risposta di @Juan Munhoes Junior, si può camminare la gerarchia, ma ci possono essere diversi percorsi che l'utente potrebbe assumere, in modo che è una risposta piuttosto fragile. Non è difficile estendere questa semplice soluzione, anche se per camminare semplicemente la gerarchia cercando il fondo della pila. Chiamando respingere da un fondo avranno tutti gli altri, anche.

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

Questo è semplice e flessibile: se si desidera cercare un particolare tipo di controller della vista nella pila, si potrebbe aggiungere la logica in base a [vc isKindOfClass:[DesiredViewControllerClass class]].

iOS 8+ metodo universale per licenziamento a schermo intero senza contesto dell'animazione sbagliato. In Objective-C e 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

Cosa c'è di sbagliato con le altre soluzioni?

Ci sono molte soluzioni, ma nessuna di esse contano contesto di congedo sbagliato così:

es. radice A -> B Presents -> Presents C e di voler annullare la A da C, si può ufficialmente chiamando dismissViewControllerAnimated su rootViewController.

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

Tuttavia chiamata licenziamento in questa radice da C porterà a un comportamento scorretto con passaggio sbagliato (B ad A sarebbe stato visto al posto di C e A).


così

Ho creato universale metodo di respingere. Questo metodo avrà corrente istantanea a tutto schermo e metterlo sopra controller di vista ha presentato il ricevitore e poi respingere tutto (Esempio: impostazione predefinita Chiamato respingere da C, ma B è davvero vista come respinge).

Di 'la tua prima controller di vista è anche la radice / Initial View Controller (quello nominato nella Storyboard come controller della vista iniziale). È possibile impostare fino a ascoltare le richieste di licenziare tutti i suoi controller di vista presentati:

in 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:^{}];
}

E in ogni altra vista del controller verso il basso la pila di navigazione che decide dovremmo tornare in cima alla pila di navigazione:

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

Questo dovrebbe licenziare tutti i controller di vista modale presentati con un'animazione, lasciando solo il controller della vista root. Questo funziona anche se il controller di vista iniziale è un'UINavigationController e il primo controller di vista è impostato come controller della vista di root.

Bonus suggerimento: E 'importante che il nome di notifica è identico. Probabilmente una buona idea per definire questo nome di notifica da qualche parte in app come una variabile, per non avere problemi di comunicazione a causa di errori di battitura.

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

È anche possibile implementare un delegato in tutti i controller di voler annullare

Se si sta utilizzando tutti sono controller di vista del modello è possibile utilizzare la notifica per respingere tutti i controller di vista preseted.

Notifica 1.Register in RootViewController come questo

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

2.Implement la funzione dismissModelViewController in RootViewController

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

3.Notifica inviare ogni chiusura o respingere evento pulsante.

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

In Swift:

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

Il problema con la maggior parte delle soluzioni è che quando si elimina la pila di viewControllers presentati, l'utente brevemente vedere il viewController prima presentata nella pila mentre viene respinta. eccellenti risolve soluzione di Jakub questo. Qui è un'estensione basato sulla sua risposta.

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)
            }
        }
    }

}

Utilizzo:. Chiamare questa funzione di estensione da qualsiasi viewController presentato che si desidera respingere di nuovo alla radice

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

Prova questo ..

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

Prima di tutto Oscar Peli grazie per il vostro codice.

Per iniziare la navigationController all'inizio, si potrebbe fare un po 'più dinamica in questo modo. (Nel caso in cui non si conosce il numero di ViewControllers in pila)

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:^{
}];

Ecco una soluzione che uso per pop e licenziare tutti i controller di vista al fine di tornare al controller della vista root. Ho questi due metodi in una categoria di 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;
    }
}

Poi mi basta chiamare

[UIViewController returnToRootViewController];

Una versione veloce con alcune aggiunte basate su questo commento

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()
        }
    }
}

Semplice ricorsiva più vicino:

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

Questo costringerà vicino ogni controller bambino e poi solo di sé animato. È possibile passare per quello che vuoi, ma se animate ogni controllore se ne vanno uno dopo l'altro ed è lento.

Chiama

baseController.dismissEntireStackAndSelf()

Swift 3 estensione sulla base delle risposte di cui sopra.

Principio per uno stack del genere: A -> B -> C -> D

  • scattare un'istantanea del D
  • Aggiungi questa istantanea su B
  • Congeda da B senza animazione
  • Al termine, respingere dalla A con l'animazione

    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 po 'tremolante sul simulatore, ma non sul dispositivo.

estensione Swift sulla base delle risposte di cui sopra:

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)
        })
    }
}

Swift versione 3.0:

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)
        }
    }
}

Completamente dimenticato il motivo per cui ho fatto questo come è incredibilmente stupida logica considerando la maggior parte del tempo presentando controller di vista di un controller di vista modale è UITabBarController rendendo questo completamente inutile. Rende molto più senso effettivamente acquisire l'istanza vista di base del controller e chiamata dismiss su questo.

Per Swift 3.0 +

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

Questo licenziare tutti i controller di vista presentati sul tuo   RootViewController.

Utilizzare questa soluzione generica per risolvere questo problema:

- (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];
    }
}

Elimina la parte superiore VC animato e gli altri no. Se le lepri tre modale VC

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

EDIT:. Se si vuole fare questo solo con un metodo, di risparmiare la gerarchia in un array di VC e respingere l'ultimo oggetto animato e gli altri non

respingere (animato: il completamento :) metodo .

Nella sezione Discussion, ha detto:

any intermediate view controllers are simply removed from the stack.
  

Se si presentano diversi controller di vista in successione, costruendo così una pila di controller di vista presentati, chiamata a questo metodo su un controller di vista abbassare nello stack respinge la sua immediata controller della vista dei bambini e tutti i controller vista sopra quel bambino in pila. Quando questo accade, solo la vista più in alto è respinto in modo animato; qualsiasi controller di vista intermedi vengono semplicemente rimossi dalla pila. Il top-più vista è respinto utilizzando il suo stile di transizione modale, che possono differire da stili usati da altri controller di vista abbassare nella pila.

In altre parole, se la pila vista del regolatore come segue

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

D chiama il metodo dismiss, tutti i controller di vista behide D, es:. (E ... Z), saranno rimossi dalla pila

Nel rapida 4 e Xcode 9 Questo ti aiuta.

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

Godetevi !!! :)

Se avete intenzione di nuovo a destra al punto di partenza, è possibile utilizzare il codice     [Self.navigationController popToRootViewControllerAnimated: YES];

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top