Как я могу получить RootViewController от выдвинутого контроллера?

137

Итак, я выдвигаю контроллер представления из RootViewController как:

[self.navigationController pushViewController: еще один анимированный вид ViewController: ДА];

Но anotherViewControllerтеперь я хочу снова получить доступ к RootViewController.

я стараюсь

// (теперь внутри anotherViewController)
/// RootViewController * root = (RootViewController *) self.parentViewController; // Нет.
// ошибка
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // ДА!! оно работает

Я не уверен, ПОЧЕМУ это работает, и я не уверен, что это лучший способ сделать это. Может кто-нибудь прокомментировать лучший способ получить RootViewController от контроллера, который вы вставили в навигационный контроллер этого RootViewController, и является ли способ, которым я это сделал, надежным или нет?

bobobobo
источник
То, что вы сделали, надежно получит контроллер корневого представления (первый в иерархии навигации), если вы хотите получить доступ к контроллеру «назад», см. Мой ответ.
Бен С

Ответы:

133

Используйте свойство viewControllers UINavigationController. Пример кода:

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

Это стандартный способ получить «задний» вид контроллера. Причина в objectAtIndex:0том, что контроллер представления, к которому вы пытаетесь получить доступ, также является корневым, если бы вы были глубже в навигации, вид сзади не совпадал бы с корневым.

Бен С
источник
6
:) ты. Это все еще кажется хакерским - :) Я действительно хотел, чтобы "официальный" член сделал работу, что-то вроде self.navigationController.rootViewController, но, увы, такого не бывает ..
bobobobo
62
Код выше является ошибочным. перед ним rootViewControllerотсутствует *, и индекс должен быть просто 0. Код в вопросе правильный:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
ma11hew28
2
Согласовано: приведенный выше код возвращает родительский контроллер представления, а не корневой контроллер представления, как запросил OP. Тем не менее, это то, что Бен С сказал, что он сделает, он просто не указал на это достаточно.
Иван Вучица,
Вторая строка должна выглядеть следующим образом: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Билли
177

Swift версия:

var rootViewController = self.navigationController?.viewControllers.first

Версия ObjectiveC:

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

Где self - это экземпляр UIViewController, встроенный в UINavigationController.

dulgan
источник
Могу ли я получить навигационный контроллер другого ViewController из другого класса?
Саскватч
Любой подкласс UIViewController будет иметь свойство navigationController, которое указывает на его первый parentViewController, соответствующий классу UINavigationController.
Дулган
12

Немного менее уродливая версия того же самого, упомянутого в почти всех этих ответах:

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

в вашем случае я бы, вероятно, сделал что-то вроде:

внутри вашего подкласса UINavigationController :

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

тогда вы можете использовать:

UIViewController *rootViewController = [self.navigationController rootViewController];

редактировать

ОП попросил недвижимость в комментариях.

если хотите, вы можете получить к нему доступ, например self.navigationController.rootViewController, просто добавив свойство readonly в свой заголовок:

@property (nonatomic, readonly, weak) UIViewController *rootViewController;
Джесси
источник
8

Для всех, кто интересуется быстрым расширением, это то, что я сейчас использую:

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first
    }
}
CSCH
источник
1
Спасибо! Также вы можете удалить «как? UIViewController»
атерешков
3

В дополнение к ответу @ dulgan , всегда полезно использовать firstObjectover objectAtIndex:0, потому что, в то время как первый возвращает nil, если в массиве нет объектов, последний выдает исключение.

UIViewController *rootViewController = self.navigationController.rootViewController;

Кроме того, было бы большим плюсом для вас создать категорию с именем UINavigationController+Additionsи определить свой метод в этом.

@interface UINavigationController (Additions)

- (UIViewController *)rootViewController;

@end

@implementation UINavigationController (Additions)

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

@end
Ozgur
источник
Я только добавил эту часть, не читая ваш ответ, вы совершенно правы, когда «firstObject» лучше, чем [0]
dulgan
1

Как насчет запроса синглтона UIApplication для его keyWindow , и из этого UIWindow запрашивать корневой контроллер представления (его свойство rootViewController ):

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];
Базилик Бурк
источник
как получить навигационный контроллер?
Естай Муратов
Это неверное предложение, так как если мой rootview-контроллер приложения - UITabBarViewController?
Сандип Сингх Рана
1

Здесь я придумал универсальный способ навигации из любого места в root.

  1. Вы создаете новый файл Class с этим классом, чтобы он был доступен из любого места в вашем проекте:

    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. Использование из любой точки вашего проекта:

    {
        ...
        SharedControllers.navigateToRoot(self)
        ...
    }
Maris
источник
0

Это сработало для меня:

Когда мой корневой контроллер представления встроен в контроллер навигации:

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

Помните, что keyWindowэто устарело.

Педро Трухильо
источник