(Objective-C) 通过 window.rootView 和 keyWindow 访问导航堆栈的区别

(Objective-C) Difference between accessing the navigation stack through the window.rootView and keyWindow

有谁知道

有什么区别
TabBarController* tabBar = (TabBarController *)_window.rootViewController;
UINavigationController* navigationController = tabBar.selectedViewController;
ViewController* initialViewController = (ViewController *)navigationController.topViewController;

还有这个

UINavigationController* navigationController = [UIApplication sharedApplication].keyWindow.rootViewController.navigationController;

ViewController* initialViewController = (ViewController *)navigationController.topViewController;

我的假设:

我的成绩:

撇开设计原则不谈(示例 B 现在看起来很老套,但到目前为止我只有这些),我真的不明白这两个示例的区别,它们的表现不应该相同吗?谁能阐明为什么这些不同?

  1. UIViewController 上的 navigationController 属性 可以为 null,因此它可能 return nil。

  2. UINavigationController上的topViewController属性也是可以为空的,return是一个UIViewController?,不是具体的class.简单地将任何 UIViewController 转换为 ViewController 是不安全的,因此您应该检查 isKindOfClass.

  3. 同样的事情适用于 rootViewController,它可能并不总是一个 TabBarController,所以无条件地转换它是不安全的。

  • 另外,发现如果 rootViewController 是 UITableViewController 类型,navigationController 是 nil。

navigationController 方法将 return 一个 parent 视图控制器,最近的父 UINavigationController。在您的情况下,根视图控制器似乎是一个 UITabBarController,每个选项卡中都有一个 UINavigationController。在选项卡控制器上调用 -navigationController 将 return nil,因为它没有父视图控制器(实际上,它是根)。

如果您的应用程序中有多个 windows,-keyWindow 可以更改。该方法在 iOS 13 中也已弃用,因为现在可以有多个 window 场景,所以如果您知道具体的 window 开始,最好使用它而不是从 UIApplication 开始并向下钻取。

编辑:如果目标是呈现一个新的视图控制器,您将从根视图控制器开始,向上走呈现的视图控制器,并在其上呈现您的新视图控制器。

@implementation UIViewController (MyOvertop)

- (void)my_presentOvertopViewController:(UIViewController *)other animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion
{
    UIViewController *topmost = self;

    while (topmost.presentedViewController &&
           ![topmost.presentedViewController isBeingDismissed])
    {
        topmost = topmost.presentedViewController;
    }

    if (topmost.transitionCoordinator) {
        // transition already going on, wait til it's done and try again
        [topmost.transitionCoordinator animateAlongsideTransition:nil completion:^(id context) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self my_presentOvertopViewController:vc animated:animated completion:completion];
            });
        }];
        return;
    }

    if (topmost != other) {
        [topmost presentViewController:other animated:animated completion:completion];
    }
}

@end

(上面可能有错别字,但就是这个意思)