Jak uzyskać RootViewController z wypychanego kontrolera?

137

Więc wypycham kontroler widoku z RootViewController w taki sposób:

[self.navigationController pushViewController: anotherViewController animowana: TAK];

ALE, OD anotherViewControllerteraz chcę ponownie uzyskać dostęp do RootViewController.

Próbuję

// (teraz wewnątrz anotherViewController)
/// RootViewController * root = (RootViewController *) self.parentViewController; // Nie.
// błąd
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // TAK!! to działa

Nie jestem pewien, DLACZEGO to działa i nie jestem pewien, czy to najlepszy sposób, aby to zrobić. Czy ktoś może skomentować lepszy sposób uzyskania RootViewController z kontrolera, który został wypchnięty do navigationController tego RootViewControllera i czy sposób, w jaki to zrobiłem, jest niezawodny, czy nie?

bobobobo
źródło
To, co zrobiłeś, niezawodnie dostanie główny kontroler widoku (pierwszy w hierarchii nawigacji), jeśli chcesz uzyskać dostęp do kontrolera widoku „wstecz”, zobacz moją odpowiedź.
Ben S

Odpowiedzi:

133

Użyj właściwości viewControllers UINavigationController. Przykładowy kod:

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

Jest to standardowy sposób uzyskiwania kontrolera widoku „wstecz”. Powodem objectAtIndex:0jest to, że kontroler widoku, do którego próbujesz uzyskać dostęp, jest również głównym, gdybyś był głębiej w nawigacji, widok z tyłu nie byłby taki sam jak widok główny.

Ben S.
źródło
6
:) ty. Wciąż wygląda to na zepsuty - :) Naprawdę chciałem, żeby wykonał tę pracę "oficjalny" członek, coś w rodzaju self.navigationController.rootViewController, ale niestety, nic takiego ...
bobobobo
62
Powyższy kod jest błędny. rootViewControllerbrakuje *przed nim, a indeks powinien być 0. Kod w pytaniu jest poprawny:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
ma11hew28
2
Zgoda: powyższy kod zwraca nadrzędny kontroler widoku, a nie główny kontroler widoku, jak poprosił OP. Mimo to Ben S powiedział, że zrobi, ale nie zwrócił na to uwagi.
Ivan Vučica
Druga linia powinna brzmieć: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Billy
177

Wersja Swift:

var rootViewController = self.navigationController?.viewControllers.first

Wersja ObjectiveC:

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

Gdzie self jest wystąpieniem UIViewController osadzonym w UINavigationController.

dulgan
źródło
Czy mogę uzyskać navigationController innego ViewController z innej klasy?
sasquatch
Każda podklasa UIViewController będzie miała właściwość navigationController, która wskazuje na jej pierwszą klasę parentViewController pasującą do klasy UINavigationController.
dulgan
12

Nieco mniej brzydka wersja tego samego, o którym mowa w prawie wszystkich tych odpowiedziach:

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

w twoim przypadku prawdopodobnie zrobiłbym coś takiego:

wewnątrz podklasy UINavigationController :

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

wtedy możesz użyć:

UIViewController *rootViewController = [self.navigationController rootViewController];

edytować

OP zapytał o nieruchomość w komentarzach.

jeśli chcesz, możesz uzyskać do tego dostęp za pomocą czegoś takiego, jak self.navigationController.rootViewControllerpo prostu dodając właściwość readonly do nagłówka:

@property (nonatomic, readonly, weak) UIViewController *rootViewController;
Jesse
źródło
8

Dla wszystkich, którzy są zainteresowani szybkim rozszerzeniem, teraz używam tego:

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}
csch
źródło
1
Dzięki! Możesz także usunąć „as? UIViewController”
atereshkov
3

Jako dodatek do odpowiedzi @ dulgan , zawsze dobrym podejściem jest użycie firstObjectover objectAtIndex:0, ponieważ podczas gdy pierwsza zwraca nil, jeśli nie ma obiektu w tablicy, druga rzuca wyjątek.

UIViewController *rootViewController = self.navigationController.rootViewController;

Alternatywnie dużym plusem byłoby utworzenie kategorii o nazwie UINavigationController+Additionsi zdefiniowanie w niej metody.

@interface UINavigationController (Additions)

- (UIViewController *)rootViewController;

@end

@implementation UINavigationController (Additions)

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

@end
ozgur
źródło
Właśnie dodałem tę część bez czytania swoją odpowiedź, jesteś całkowicie w porządku z „firstObject” bycia lepszym Thant [0]
dulgan
1

Co powiesz na zapytanie singletona UIApplication o jego keyWindow i z tego UIWindow poproś o główny kontroler widoku (jego właściwość rootViewController ):

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Basil Bourque
źródło
jak zdobyć kontroler nawigacji?
Yestay Muratov
To jest zła sugestia, bo co, jeśli mój rootviewcontroller aplikacji to UITabBarViewController?
Sandeep Singh Rana
1

Tutaj wymyśliłem uniwersalną metodę nawigacji z dowolnego miejsca do katalogu głównego.

  1. Tworzysz nowy plik Class z tą klasą, aby był dostępny z dowolnego miejsca w projekcie:

    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. Wykorzystanie z dowolnego miejsca w projekcie:

    {
        ...
        SharedControllers.navigateToRoot(self)
        ...
    }
Maris
źródło
0

To zadziałało dla mnie:

Kiedy mój główny kontroler widoku jest osadzony w kontrolerze nawigacji:

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

Pamiętaj, że keyWindowjest to przestarzałe.

Pedro Trujillo
źródło