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?


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.


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.


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;

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

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

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


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

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.