Pregunta

I tienen una larga jerarquía de vistas controladores;

en el primer controlador de vista utilizo este código:

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

En el segundo controlador de vista utilizo este código:

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

y así sucesivamente.

Así que hay un momento en el que tengo muchas Ver los controladores y tengo que volver a la primera vista, controlador. Si vuelvo un paso a la vez, que utilizo en cada controlador de vista este código:

[self dismissModalViewControllerAnimated:YES];

Si yo quiero volver directamente de la, digamos, sexto Vista Controlador a la primera, lo que tengo que hacer para despedir a todos los controladores a la vez?

Gracias

¿Fue útil?

Solución 2

I encontró la solución.

Por supuesto, se puede encontrar la solución en la mayoría lugar obvio lo que la lectura de la referencia UIViewController para el método dismissModalViewControllerAnimated ...

Si presenta varios vista modal controladores en sucesión, y por lo tanto construcción de una pila de vista modal controladores, llamar a este método en una controlador de vista inferior en la pila despide a su visión inmediata del niño controlador y todos los controladores de vista por encima de ese niño en la pila. Cuando esto sucede, sólo el más a la vista superior es despedido de una manera animada; cualquier controladores de vista intermedios son simplemente retirado de la pila. los más superficial de vista se descartó el uso de su estilo de transición modal, lo que puede diferir de los estilos utilizados por otra controladores de vista inferior en la pila.

por lo que es suficiente para llamar a la dismissModalViewControllerAnimated en el objetivo Vista. He utilizado el siguiente código:

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

para volver a mi casa.

Otros consejos

Sí. ya hay un montón de respuestas, pero sólo voy a añadir una a la final de la lista de todos modos. El problema es que necesitamos para obtener una referencia al controlador de vista en la base de la jerarquía. Al igual que en la respuesta de @Juan Munhoes Junior, se puede caminar por la jerarquía, pero puede haber diferentes rutas usuario puede durar, por lo que es una respuesta bastante frágil. No es difícil de extender esta solución simple, aunque simplemente caminar la jerarquía en busca de la parte inferior de la pila. Llamar a despedir, por una parte inferior recibirá todos los demás, también.

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

Esto es simple y flexible: si desea buscar un tipo particular de controlador de vista de la pila, se podría añadir lógica basada en [vc isKindOfClass:[DesiredViewControllerClass class]].

método universal para iOS 8+ por despido pantalla completa sin un contexto de animación mal. En Objective-C y 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é hay de malo en otras soluciones?

Hay muchas soluciones, pero ninguna de ellas cuentan con el contexto Dismissing mal, así:

por ejemplo. A raíz -> Presenta B -> Presenta C y quiere despedir a la A de C, que puede oficialmente llamando dismissViewControllerAnimated en rootViewController.

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

Sin embargo Llamada de despido a esta raíz de C dará lugar a un comportamiento correcto con la transición mal (B a A se habrían visto en lugar de C a A).


modo

He creado universal de despedir método. Este método tomar instantáneas de pantalla completa actual y colocarlo sobre controlador de vista presentada del receptor y luego desechar todo (Ejemplo: default Llamado despedir de C, pero B es realmente visto como despedir).

Digamos que su primer controlador de vista es también la raíz / Vista Controlador inicial (el que nominado en el guión gráfico como el controlador de vista inicial). Puede configurarlo para escuchar las peticiones para despedir a todos sus controladores de vista presentados:

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

Y en cualquier otro controlador de vista abajo de la pila de navegación que decide que deberíamos volver a la parte superior de la pila de desplazamiento:

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

Esto debería despedir a todos los controladores de vista modal presentados con una animación, dejando sólo el controlador de vista raíz. Esto también funciona si su controlador de vista inicial es una UINavigationController y el primer controlador de vista se establece como su controlador de vista raíz.

Consejo adicional: Es importante que el nombre de la notificación es idéntica. Probablemente una buena idea para definir el nombre de la notificación en algún lugar de la aplicación como una variable, que no se consigue la falta de comunicación debido a los errores de escritura.

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

También puede aplicar un delegado en todos los controladores que desea descartar

Si está utilizando todos son controlador de vista de modelo se puede utilizar la notificación para despedir a toda controlador de vista preseted.

1.Register Notificación en RootViewController como esto

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

2.Implement la función dismissModelViewController en RootViewController

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

3.Notificación correos cada cerrar o despedir evento de botón.

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

En Swift:

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

El problema con la mayoría de las soluciones es que cuando se desecha la pila de viewControllers presentados, el usuario verá brevemente el viewController presentado por primera vez en la pila a medida que se desestimó. excelentes resuelve solución de jakub eso. Aquí es una extensión basada en su respuesta.

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

}

Uso:. Llama a esta función de extensión de cualquier viewController presentado que desea borrar de nuevo a la raíz

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

Probar ..

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

En primer lugar los Oscar Peli gracias por su código.

Para iniciar su navigationController al principio, se podría hacer un poco más dinámica de esta manera. (En caso de que no se conoce el número de ViewControllers en 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:^{
}];

Esta es una solución que utilizo para hacer estallar y despedir a todos los controladores de vista con el fin de volver al controlador de vista raíz. Tengo esos dos métodos en una categoría 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;
    }
}

A continuación, acabo de llamar

[UIViewController returnToRootViewController];

Una versión rápida con algunas adiciones en base a este comentario

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

recursivo simple cercano:

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

Esto obligará a cerca de todos los controladores de niño y sólo sí animado. Puede alternar para lo que quiera, pero si se animan cada controlador que ir uno por uno y es lento.

Llamar

baseController.dismissEntireStackAndSelf()

Swift 3 extensión sobre la base de las respuestas anteriores.

Principio para una pila como que: A -> B -> C -> D

  • Tome una instantánea de D
  • Añadir esta instantánea en B
  • Descartar de B sin animación
  • Una vez finalizada, despedir de la A con la animación

    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 poco vacilante en el simulador, pero no en el dispositivo.

Extensiones de Swift en base a las respuestas anteriores:

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 versión 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)
        }
    }
}

olvidó por completo por qué hice esto, ya que es increíblemente estúpida lógica considerando que la mayoría de las veces controlador de vista de la presentación de un controlador de vista modal es UITabBarController haciendo esto completamente inútil. Tiene mucho más sentido de realidad adquirir la instancia controlador de vista base y dismiss llamada en eso.

Para Swift 3,0 +

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

Esto despedir a todos los controladores de vista presentados en su RootViewController.

Utilice esta solución genérica para resolver este 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];
    }
}

Descartar la parte superior VC animado y los otros no. Si las liebres tres modal VC

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

EDIT:. Si usted quiere hacer esto sólo con un método, excepto que la jerarquía en una matriz de VC y despedir el último objeto animado y los otros no

despedir (animado: Método finalización :) .

En la sección Discussion, dijo:

any intermediate view controllers are simply removed from the stack.

Si presenta varios controladores de vista en la serie, construyendo así una pila de controladores de vista presentados, llamar a este método en un controlador de vista abajo en la pila descarta su inmediata controlador de vista del niño y todos los controladores de vista por encima de ese niño en la pila. Cuando esto sucede, sólo la vista de más arriba es despedido de una manera animada; cualquier controladores de vista intermedios se eliminan simplemente de la pila. El-más la vista superior se descartó el uso de su estilo de transición modal, que pueden diferir de los estilos utilizados por otros controladores de vista inferior en la pila.

En otras palabras, si la pila controlador de vista como siguiente

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

D llama al método dismiss, todos los controladores de vista behide D, ex:. (E ... Z), será retirado de la pila

veloz 4 y Xcode 9 Esto le ayuda.

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

Disfrute !!! :)

Si vas a volver a la derecha de la salida, se puede utilizar el código [Self.navigationController popToRootViewControllerAnimated: YES];

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top