Как я могу получить 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, и является ли способ, которым я это сделал, надежным или нет?


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

Ответы:


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.


Могу ли я получить навигационный контроллер другого 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
    }
}

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

Я только добавил эту часть, не читая ваш ответ, вы совершенно правы, когда «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)
        ...
    }

0

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.