¿Cómo obtengo el RootViewController de un controlador push?

137

Entonces, empujo un controlador de vista de RootViewController como:

[self.navigationController pushViewController: otroViewController animado: SÍ];

PERO, DESDE anotherViewControllerahora, quiero acceder al RootViewController nuevamente.

Lo estoy intentando

// (ahora dentro de otro ViewController)
/// RootViewController * root = (RootViewController *) self.parentViewController; // No.
// err
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // ¡¡SI!! funciona

No estoy seguro de POR QUÉ esto funciona y no estoy seguro de si es la mejor manera de hacerlo. ¿Alguien puede comentar sobre una mejor manera de obtener el RootViewController de un controlador que ha introducido en ese RootViewController navigationController y si la forma en que lo hice es confiable o no?

bobobobo
fuente
Lo que ha hecho obtendrá de forma confiable el controlador de vista raíz (el primero en la jerarquía de navegación), si desea obtener acceso al controlador de vista "posterior", vea mi respuesta.
Ben S

Respuestas:

133

Use la propiedad viewControllers de UINavigationController. Código de ejemplo:

// Inside another ViewController
NSArray *viewControllers = self.navigationController.viewControllers;
UIViewController *rootViewController = [viewControllers objectAtIndex:viewControllers.count - 2];

Esta es la forma estándar de obtener el controlador de vista "posterior". La razón objectAtIndex:0funciona porque el controlador de vista al que está intentando acceder también es el raíz, si fuera más profundo en la navegación, la vista posterior no sería la misma que la vista raíz.

Ben S
fuente
66
:) ty. Todavía parece hacky - :) Realmente quería un miembro "oficial" para hacer el trabajo, algo así como self.navigationController.rootViewController, pero por desgracia, no hay tal cosa ..
bobobobo
62
El código anterior es erróneo. rootViewControllerfalta *antes, y el índice debería estar solo 0. El código en la pregunta es correcto:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
ma11hew28
2
De acuerdo: el código anterior devuelve el controlador de vista principal , no el controlador de vista raíz como lo solicitó el OP. Aún así, eso es lo que Ben S dijo que haría, simplemente no lo señaló lo suficiente.
Ivan Vučica
La segunda línea debe leer: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Billy
177

Versión rápida:

var rootViewController = self.navigationController?.viewControllers.first

Versión ObjectiveC:

UIViewController *rootViewController = [self.navigationController.viewControllers firstObject];

Donde self es una instancia de un UIViewController incrustado en un UINavigationController.

dulgan
fuente
¿Puedo obtener navigationController de otro ViewController de una clase diferente?
sasquatch
Cualquier subclase UIViewController tendrá una propiedad navigationController, que apunta a su primera clase parentViewController que coincida con la clase UINavigationController.
dulgan
12

Una versión un poco menos fea de lo mismo mencionado en casi todas estas respuestas:

UIViewController *rootViewController = [[self.navigationController viewControllers] firstObject];

en su caso, probablemente haría algo como:

dentro de su subclase UINavigationController :

- (UIViewController *)rootViewController
{
    return [[self viewControllers] firstObject];
}

entonces puedes usar:

UIViewController *rootViewController = [self.navigationController rootViewController];

editar

OP solicitó una propiedad en los comentarios.

si lo desea, puede acceder a esto a través de algo como self.navigationController.rootViewControllersimplemente agregando una propiedad de solo lectura a su encabezado:

@property (nonatomic, readonly, weak) UIViewController *rootViewController;
Jesse
fuente
8

Para todos los que estén interesados ​​en una extensión rápida, esto es lo que estoy usando ahora:

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}
csch
fuente
1
¡Gracias! También puede eliminar "como? UIViewController"
atereshkov
3

Como una adición a la respuesta de @ dulgan , siempre es un buen método para usar firstObjectover objectAtIndex:0, porque mientras que el primero devuelve nil si no hay ningún objeto en la matriz, el último arroja una excepción.

UIViewController *rootViewController = self.navigationController.rootViewController;

Alternativamente, sería una gran ventaja para usted crear una categoría llamada UINavigationController+Additionsy definir su método en eso.

@interface UINavigationController (Additions)

- (UIViewController *)rootViewController;

@end

@implementation UINavigationController (Additions)

- (UIViewController *)rootViewController
{
    return self.viewControllers.firstObject;
}

@end
ozgur
fuente
Acabo de agregar esta parte sin leer su respuesta, tiene toda la razón con el "primer objeto" siendo mejor que [0]
dulgan
1

¿Qué tal pedirle al singleton UIApplication su keyWindow , y desde ese UIWindow pedir el controlador de vista raíz (su propiedad rootViewController ):

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Albahaca Bourque
fuente
¿Cómo obtener el controlador de navegación?
Yestay Muratov
Esta es una sugerencia incorrecta, ¿y si mi rootviewcontroller de aplicación es UITabBarViewController?
Sandeep Singh Rana
1

Aquí se me ocurrió un método universal para navegar desde cualquier lugar a la raíz.

  1. Crea un nuevo archivo de clase con esta clase, para que sea accesible desde cualquier parte de su proyecto:

    import UIKit
    
    class SharedControllers
    {
        static func navigateToRoot(viewController: UIViewController)
        {
            var nc = viewController.navigationController
    
            // If this is a normal view with NavigationController, then we just pop to root.
            if nc != nil
            {
                nc?.popToRootViewControllerAnimated(true)
                return
            }
    
            // Most likely we are in Modal view, so we will need to search for a view with NavigationController.
            let vc = viewController.presentingViewController
    
            if nc == nil
            {
                nc = viewController.presentingViewController?.navigationController
            }
    
            if nc == nil
            {
                nc = viewController.parentViewController?.navigationController
            }
    
            if vc is UINavigationController && nc == nil
            {
                nc = vc as? UINavigationController
            }
    
            if nc != nil
            {
                viewController.dismissViewControllerAnimated(false, completion:
                    {
                        nc?.popToRootViewControllerAnimated(true)
                })
            }
        }
    }
  2. Uso desde cualquier parte de su proyecto:

    {
        ...
        SharedControllers.navigateToRoot(self)
        ...
    }
Maris
fuente
0

Esto funcionó para mí:

Cuando mi controlador de vista raíz está incrustado en un controlador de navegación:

UINavigationController * navigationController = (UINavigationController *)[[[[UIApplication sharedApplication] windows] firstObject] rootViewController];
RootViewController * rootVC = (RootViewController *)[[navigationController viewControllers] firstObject];

Recuerda que keyWindowestá en desuso.

Pedro Trujillo
fuente