Wie bekomme ich den RootViewController von einem Push-Controller?

137

Also schiebe ich einen View Controller von RootViewController wie folgt:

[self.navigationController pushViewController: anotherViewController animiert: YES];

ABER ab anotherViewControllersofort möchte ich wieder auf den RootViewController zugreifen.

Ich versuche

// (jetzt in einem anderen ViewController)
/// RootViewController * root = (RootViewController *) self.parentViewController; // Nein.
// irren
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // JA!! Es klappt

Ich bin mir nicht sicher, WARUM das funktioniert und ich bin mir nicht sicher, ob es der beste Weg ist, dies zu tun. Kann jemand einen besseren Weg kommentieren, um den RootViewController von einem Controller zu erhalten, den Sie in den Navigationscontroller dieses RootViewControllers geschoben haben, und ob die Art und Weise, wie ich es gemacht habe, zuverlässig ist oder nicht?

Bobobobo
quelle
Was Sie getan haben, wird zuverlässig den Root-View-Controller (den ersten in der Navigationshierarchie) erhalten. Wenn Sie Zugriff auf den "Back" -Ansicht-Controller erhalten möchten, lesen Sie meine Antwort.
Ben S

Antworten:

133

Verwenden Sie die viewControllers- Eigenschaft des UINavigationController. Beispielcode:

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

Dies ist die Standardmethode, um den "Back" -Ansichts-Controller zu erhalten. Der Grund dafür objectAtIndex:0ist, dass der Ansichts-Controller, auf den Sie zugreifen möchten, auch der Stamm-Controller ist. Wenn Sie tiefer in der Navigation wären, wäre die Rückansicht nicht mit der Stammansicht identisch.

Ben S.
quelle
6
:) ty. Es scheint immer noch hacky - :) Ich wollte wirklich, dass ein "offizielles" Mitglied den Job macht, so etwas wie self.navigationController.rootViewController, aber leider nicht so etwas ..
Bobobobo
62
Der obige Code ist fehlerhaft. rootViewControllerfehlt *davor, und der Index sollte nur sein 0. Der Code in der Frage ist korrekt:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
ma11hew28
2
Einverstanden: Der obige Code gibt den übergeordneten Ansichts-Controller zurück, nicht den Root- Ansichts-Controller, wie vom OP angefordert. Trotzdem hat Ben S gesagt, dass er es tun wird, er hat einfach nicht genug darauf hingewiesen.
Ivan Vučica
Die zweite Zeile sollte lauten: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Billy
177

Schnelle Version:

var rootViewController = self.navigationController?.viewControllers.first

ObjectiveC-Version:

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

Wobei self eine Instanz eines UIViewControllers ist, der in einen UINavigationController eingebettet ist.

Dulgan
quelle
Kann ich den Navigationscontroller eines anderen ViewControllers aus einer anderen Klasse erhalten?
Sasquatch
Jede UIViewController-Unterklasse verfügt über eine navigationController-Eigenschaft, die auf den ersten parentViewController verweist, der der UINavigationController-Klasse entspricht.
Dulgan
12

Eine etwas weniger hässliche Version derselben Sache, die in so ziemlich allen diesen Antworten erwähnt wurde:

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

In Ihrem Fall würde ich wahrscheinlich so etwas tun:

in Ihrer UINavigationController-Unterklasse :

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

dann können Sie verwenden:

UIViewController *rootViewController = [self.navigationController rootViewController];

bearbeiten

OP fragte in den Kommentaren nach einer Eigenschaft.

Wenn Sie möchten, können Sie darauf zugreifen, self.navigationController.rootViewControllerindem Sie Ihrem Header einfach eine schreibgeschützte Eigenschaft hinzufügen:

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

Für alle, die an einer schnellen Erweiterung interessiert sind, ist dies das, was ich jetzt benutze:

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}
csch
quelle
1
Vielen Dank! Sie können auch "as? UIViewController"
atereshkov
3

Als Ergänzung zu @ dulgans Antwort ist es immer ein guter Ansatz, firstObjectover zu verwenden objectAtIndex:0, da der erste Wert nil zurückgibt, wenn sich kein Objekt im Array befindet, der letztere eine Ausnahme auslöst.

UIViewController *rootViewController = self.navigationController.rootViewController;

Alternativ wäre es ein großes Plus für Sie, eine Kategorie mit dem Namen zu erstellen UINavigationController+Additionsund Ihre Methode darin zu definieren.

@interface UINavigationController (Additions)

- (UIViewController *)rootViewController;

@end

@implementation UINavigationController (Additions)

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

@end
Ozgur
quelle
Ich habe gerade diesen Teil hinzugefügt, ohne Ihre Antwort zu lesen. Sie haben völlig Recht damit, dass das "erste Objekt" besser ist als [0]
Dulgan
1

Wie wäre es, wenn Sie den UIApplication- Singleton nach seinem keyWindow fragen und von diesem UIWindow aus nach dem Root-View-Controller (seiner rootViewController- Eigenschaft) fragen :

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Basil Bourque
quelle
Wie bekomme ich einen Navigations-Controller?
Yestay Muratov
Dies ist ein falscher Vorschlag. Was ist, wenn mein Rootviewcontroller der Anwendung UITabBarViewController ist?
Sandeep Singh Rana
1

Hier habe ich mir eine universelle Methode ausgedacht, um von jedem Ort zur Wurzel zu navigieren.

  1. Mit dieser Klasse erstellen Sie eine neue Klassendatei, sodass Sie von überall in Ihrem Projekt darauf zugreifen können:

    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. Verwendung von überall in Ihrem Projekt:

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

Das hat bei mir funktioniert:

Wenn mein Root View Controller in einen Navigationscontroller eingebettet ist:

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

Denken Sie daran, dass dies keyWindowveraltet ist.

Pedro Trujillo
quelle