Como obtenho o RootViewController de um controlador enviado?

137

Então, eu envio um controlador de exibição do RootViewController como:

[self.navigationController pushViewController: outroViewController animado: YES];

MAS, a partir de anotherViewControlleragora, quero acessar o RootViewController novamente.

estou tentando

// (dentro de outroViewController agora)
/// RootViewController * root = (RootViewController *) self.parentViewController; // Não.
// err
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // SIM!! funciona

Não sei por que isso funciona e não tenho certeza se é a melhor maneira de fazê-lo. Alguém pode comentar sobre uma maneira melhor de obter o RootViewController de um controlador que você inseriu no navegador de navegação do RootViewController e se a maneira como eu fiz isso é confiável ou não?

bobobobo
fonte
O que você fez obterá de forma confiável o controlador de visualização raiz (o primeiro na hierarquia de navegação). Se você deseja obter acesso ao controlador de visualização "traseiro", veja minha resposta.
217 Ben Ben

Respostas:

133

Use a propriedade viewControllers do UINavigationController. Código de exemplo:

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

Essa é a maneira padrão de obter o controlador de exibição "traseiro". O motivo objectAtIndex:0é que o controlador de visualização que você está tentando acessar também é o raiz; se você estivesse mais aprofundado na navegação, a visualização traseira não seria a mesma que a visualização raiz.

Ben S
fonte
6
:) ty. Ainda parece hacky - :) Eu realmente queria um membro "oficial" para fazer o trabalho, algo como self.navigationController.rootViewController, mas, infelizmente, há tal coisa ..
bobobobo
62
O código acima está incorreto. rootViewControllerestá faltando *antes, e o índice deveria estar 0. O código da pergunta está correto:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
ma11hew28
2
Concordado: o código acima retorna o controlador de visualização pai , não o controlador de visualização raiz, conforme solicitado pelo OP. Ainda assim, foi o que Ben S disse que faria, ele simplesmente não apontou isso o suficiente.
Ivan Vučica
A segunda linha deve ser: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Billy
177

Versão rápida:

var rootViewController = self.navigationController?.viewControllers.first

Versão ObjectiveC:

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

Onde self é uma instância de um UIViewController incorporado em um UINavigationController.

dulgan
fonte
Posso obter o navigationController de outro ViewController de uma classe diferente?
Sasquatch #
Qualquer subclasse UIViewController terá uma propriedade navigationController, que aponta para sua primeira parentViewController correspondente à classe UINavigationController.
dulgan
12

Uma versão um pouco menos feia da mesma coisa mencionada em praticamente todas essas respostas:

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

no seu caso, eu provavelmente faria algo como:

dentro de sua subclasse UINavigationController :

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

então você pode usar:

UIViewController *rootViewController = [self.navigationController rootViewController];

editar

O OP pediu uma propriedade nos comentários.

se quiser, você pode acessá-lo por meio de algo como self.navigationController.rootViewControllerapenas adicionando uma propriedade somente leitura ao seu cabeçalho:

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

Para todos os interessados ​​em uma extensão rápida, é isso que estou usando agora:

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}
csch
fonte
1
Obrigado! Você também pode remover "as? UIViewController"
atereshkov
3

Como complemento à resposta de @ dulgan , é sempre uma boa abordagem usar firstObjectdemais objectAtIndex:0, porque, enquanto o primeiro retorna nulo se não houver objeto na matriz, o último gera uma exceção.

UIViewController *rootViewController = self.navigationController.rootViewController;

Como alternativa, seria uma grande vantagem para você criar uma categoria nomeada UINavigationController+Additionse definir seu método nela .

@interface UINavigationController (Additions)

- (UIViewController *)rootViewController;

@end

@implementation UINavigationController (Additions)

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

@end
ozgur
fonte
Acabei de adicionar esta parte sem ler sua resposta, você está totalmente certo com o "firstObject" ser melhor thant [0]
dulgan
1

Que tal pedir ao UIApplication singleton sua keyWindow e, a partir dessa UIWindow, solicitar o controlador de exibição raiz (sua propriedade rootViewController ):

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Basil Bourque
fonte
como obter o controlador de navegação?
Yestay Muratov 02/12/19
Esta é uma sugestão errada, e se o meu rootviewcontroller do aplicativo for UITabBarViewController?
Sandeep Singh Rana
1

Aqui eu vim com o método universal para navegar de qualquer lugar até a raiz.

  1. Você cria um novo arquivo de classe com essa classe, para que seja acessível de qualquer lugar do seu projeto:

    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 de qualquer lugar do seu projeto:

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

Isso funcionou para mim:

Quando meu controlador de visualização raiz está incorporado em um controlador de navegação:

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

Lembre-se de que keyWindowestá obsoleto.

Pedro Trujillo
fonte