Comment obtenir le RootViewController à partir d'un contrôleur poussé?

137

Donc, je pousse un contrôleur de vue de RootViewController comme:

[self.navigationController pushViewController: anotherViewController animé: OUI];

MAIS, À PARTIR de anotherViewControllermaintenant, je veux accéder à nouveau au RootViewController.

j'essaie

// (à l'intérieur d'un autreViewController maintenant)
/// RootViewController * root = (RootViewController *) self.parentViewController; // Non.
// err
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // OUI!! Ça marche

Je ne sais pas pourquoi cela fonctionne et je ne suis pas sûr que ce soit la meilleure façon de le faire. Quelqu'un peut-il commenter une meilleure façon d'obtenir le RootViewController d'un contrôleur que vous avez poussé dans le navigationController de ce RootViewController et si la façon dont je l'ai fait est fiable ou non?

bobobobo
la source
Ce que vous avez fait obtiendra de manière fiable le contrôleur de vue racine (le premier dans la hiérarchie de navigation), si vous voulez accéder au contrôleur de vue «arrière», voir ma réponse.
Ben S

Réponses:

133

Utilisez la propriété viewControllers de UINavigationController. Exemple de code:

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

C'est la manière standard d'obtenir le contrôleur de vue «arrière». La raison objectAtIndex:0fonctionne parce que le contrôleur de vue auquel vous essayez d'accéder est également le contrôleur racine, si vous étiez plus profond dans la navigation, la vue arrière ne serait pas la même que la vue racine.

Ben S
la source
6
:) ty. Cela semble toujours hacky - :) Je voulais vraiment qu'un membre "officiel" fasse le travail, quelque chose comme self.navigationController.rootViewController, mais hélas, rien de tel ..
bobobobo
62
Le code ci-dessus est erroné. rootViewControllerest manquant *avant lui, et l'index devrait simplement l'être 0. Le code de la question est correct:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
ma11hew28
2
D'accord: le code ci-dessus renvoie le contrôleur de vue parent , pas le contrôleur de vue racine comme l'OP l'a demandé. Pourtant, c'est ce que Ben S a dit qu'il ferait, il ne l'a tout simplement pas assez souligné.
Ivan Vučica
La deuxième ligne doit lire: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Billy
177

Version Swift:

var rootViewController = self.navigationController?.viewControllers.first

Version ObjectiveC:

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

Où self est une instance d'un UIViewController incorporé dans un UINavigationController.

Dulgan
la source
Puis-je obtenir navigationController d'un autre ViewController d'une classe différente?
sasquatch
Toute sous-classe UIViewController aura une propriété navigationController, qui pointe vers son premier parentViewController correspondant à la classe UINavigationController.
dulgan
12

Une version légèrement moins moche de la même chose mentionnée dans à peu près toutes ces réponses:

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

dans votre cas, je ferais probablement quelque chose comme:

dans votre sous - classe UINavigationController :

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

alors vous pouvez utiliser:

UIViewController *rootViewController = [self.navigationController rootViewController];

Éditer

OP a demandé une propriété dans les commentaires.

si vous le souhaitez, vous pouvez y accéder via quelque chose comme self.navigationController.rootViewControlleren ajoutant simplement une propriété en lecture seule à votre en-tête:

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

Pour tous ceux qui sont intéressés par une extension rapide, voici ce que j'utilise maintenant:

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}
csch
la source
1
Merci! Vous pouvez également supprimer "as? UIViewController"
atereshkov
3

En complément de la réponse de @ dulgan , c'est toujours une bonne approche à utiliser firstObjectover objectAtIndex:0, car alors que le premier renvoie nil s'il n'y a pas d'objet dans le tableau, le dernier lève une exception.

UIViewController *rootViewController = self.navigationController.rootViewController;

Sinon, ce serait un gros plus pour vous de créer une catégorie nommée UINavigationController+Additionset de définir votre méthode dans celle-ci.

@interface UINavigationController (Additions)

- (UIViewController *)rootViewController;

@end

@implementation UINavigationController (Additions)

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

@end
Ozgur
la source
Je viens d'ajouter cette partie sans lire votre réponse, vous avez tout à fait raison avec le "firstObject" étant meilleur que [0]
dulgan
1

Que diriez-vous de demander au singleton UIApplication pour sa keyWindow , et à partir de cette UIWindow, demandez le contrôleur de vue racine (sa propriété rootViewController ):

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Basil Bourque
la source
comment obtenir le contrôleur de navigation?
Yestay Muratov
C'est une mauvaise suggestion car que faire si mon rootviewcontroller d'application est UITabBarViewController?
Sandeep Singh Rana
1

Ici, j'ai trouvé une méthode universelle pour naviguer de n'importe quel endroit à root.

  1. Vous créez un nouveau fichier de classe avec cette classe, afin qu'il soit accessible de n'importe où dans votre projet:

    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. Utilisation depuis n'importe où dans votre projet:

    {
        ...
        SharedControllers.navigateToRoot(self)
        ...
    }
Maris
la source
0

Cela a fonctionné pour moi:

Lorsque mon contrôleur de vue racine est intégré dans un contrôleur de navigation:

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

N'oubliez pas que keyWindowc'est obsolète.

Pedro Trujillo
la source