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

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

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

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

Я пытаюсь

// (inside anotherViewController now)
///RootViewController *root = (RootViewController*)self.parentViewController ; // No.
// err
RootViewController *root = (RootViewController*)[self.navigationController.viewControllers objectAtIndex:0] ; // YES!! it works

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

Ответ 1

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

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

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

Ответ 2

Быстрая версия:

var rootViewController = self.navigationController?.viewControllers.first

Версия ObjectiveC:

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

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

Ответ 3

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

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

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

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

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

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

UIViewController *rootViewController = [self.navigationController rootViewController];

изменить

OP запросил свойство в комментариях.

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

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

Ответ 4

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

extension UINavigationController {
    var rootViewController : UIViewController? {
        return self.viewControllers.first as? UIViewController
    }
}

Ответ 5

В дополнение к ответу @dulgan, это всегда хороший подход - использовать firstObject 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

Ответ 6

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

UIViewController root = [[[UIApplication sharedApplication] keyWindow] rootViewController];

Ответ 7

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

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

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

    {
        ...
        SharedControllers.navigateToRoot(self)
        ...
    }