Як отримати RootViewController від натиснутого контролера?


137

Отже, я натискаю на контролер перегляду від RootViewController:

[self.navigationController pushViewController: анімований іншийViewController: ТАК];

АЛЕ З цього anotherViewControllerчасу я хочу знову отримати доступ до RootViewController.

я намагаюсь

// (усередині іншогоViewController зараз)
/// RootViewController * root = (RootViewController *) self.parentViewController; // Немає.
// помилка
RootViewController * root = (RootViewController *) [self.navigationController.viewControllers objectAtIndex: 0]; // ТАК!! це працює

Я не впевнений, Чому це працює, і не впевнений, чи найкращий спосіб це зробити. Чи може хтось прокоментувати кращий спосіб отримати RootViewController від контролера, який ви натиснули на навігаційне керування RootViewController, і чи надійний це чи ні?


Що ви зробили, надійно отримаєте кореневий контролер перегляду (перший в ієрархії навігації), якщо ви хочете отримати доступ до "назад" контролера перегляду, дивіться мою відповідь.
Ben S

Відповіді:


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
Погоджено: вищевказаний код повертає батьківський контролер подання, а не контролер кореневого виду, як запитував ОП. Але все-таки це сказав Бен С, він просто цього не зазначив.
Іван Вучиця

2-й рядок повинен читати: UIViewController * rootViewController = [viewControllers objectAtIndex: viewControllers.count - 1];
Біллі

177

Швидка версія:

var rootViewController = self.navigationController?.viewControllers.first

Версія ObjectiveC:

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

Якщо self - це екземпляр UIViewController, вбудований в UINavigationController.


Чи можу я отримати NaviController іншого ViewController від іншого класу?
sasquatch

Будь-який підклас UIViewController матиме властивість navigationController, яка вказує на його перший parentViewController, що відповідає класу UINavigationController.
dulgan

12

Трохи менш потворна версія тієї ж речі, згаданої в майже всіх цих відповідях:

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

у вашому випадку я, мабуть, зробив би щось на кшталт:

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

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

тоді ви можете використовувати:

UIViewController *rootViewController = [self.navigationController rootViewController];

редагувати

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

якщо вам подобається, ви можете отримати доступ до цього через щось подібне self.navigationController.rootViewController, просто додавши властивість для читання лише у свій заголовок:

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

8

Для всіх, хто зацікавлений у швидкому розширенні, ось що я зараз використовую:

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

1
Дякую! Також ви можете видалити "як? UIViewController"
atereshkov

3

На додаток до @ 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

Я щойно додав цю частину, не читаючи вашої відповіді, ви абсолютно праві, коли "firstObject" краще, ніж thant [0]
dulgan

1

Як щодо попросивши UIApplication Сінглтона для його keyWindow , і від цього UIWindow просить контролер зору кореня (його RootViewController власності):

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

як отримати контролер навігації?
Yestay Muratov

Це неправильна пропозиція. Що робити, якщо моїм кореневим контролером програми є UITabBarViewController?
Sandeep Singh Rana

1

Тут я придумав універсальний метод для навігації з будь-якого місця на корінь.

  1. Ви створюєте новий файл класу з цим класом, щоб він був доступний з будь-якої точки вашого проекту:

    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.