【问题标题】:How do I get the RootViewController from a pushed controller?如何从推送的控制器中获取 RootViewController?
【发布时间】:2010-12-20 01:19:44
【问题描述】:

所以,我从 RootViewController 推送一个视图控制器,例如:

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

但是,现在来自anotherViewController,我想再次访问 RootViewController。

我在努力

//(现在在另一个视图控制器中) ///RootViewController *root = (RootViewController*)self.parentViewController ; // 不。 // 呃 RootViewController *root = (RootViewController*)[self.navigationController.viewControllers objectAtIndex:0] ; // 是的!!有用

我不确定为什么这样做有效,我不确定这是否是最好的方法。有人可以评论从您推送到该 RootViewController 的 navigationController 的控制器中获取 RootViewController 的更好方法以及我所做的方式是否可靠?

【问题讨论】:

标签: ios iphone uinavigationcontroller


【解决方案1】:

斯威夫特版本:

var rootViewController = self.navigationController?.viewControllers.first

ObjectiveC 版本:

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

其中 self 是嵌入在 UINavigationController 中的 UIViewController 的实例。

【讨论】:

  • 我可以从不同的类中获取另一个 ViewController 的 navigationController 吗?
  • 任何 UIViewController 子类都会有一个 navigationController 属性,它指向它的第一个 parentViewController 匹配 UINavigationController 类。
【解决方案2】:

使用 UINavigationController 的 viewControllers 属性。示例代码:

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

这是获取“后退”视图控制器的标准方法。 objectAtIndex:0 起作用的原因是因为您尝试访问的视图控制器也是根视图,如果您在导航中更深入,后视图将与根视图不同。

【讨论】:

  • :) 泰。它仍然看起来很老套 - :) 我真的想要一个“官方”成员来完成这项工作,比如 self.navigationController.rootViewController,但是唉,没有这样的事情..
  • 上面的代码是错误的。 rootViewController 前面缺少*,索引应该只是0。问题中的代码是正确的:RootViewController *root = (RootViewController *)[navigationController.viewControllers objectAtIndex:0]
  • 同意:上面的代码返回 parent 视图控制器,而不是 OP 要求的 root 视图控制器。不过,这就是 Ben S 所说的他会做的事情,他只是没有足够地指出这一点。
  • 第二行应该是:UIViewController *rootViewController = [viewControllers objectAtIndex:viewControllers.count - 1];
【解决方案3】:

几乎所有这些答案中都提到了同一件事的稍微不那么丑陋的版本:

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

在你的情况下,我可能会这样做:

在您的 UINavigationController 子类中

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

那么你可以使用:

UIViewController *rootViewController = [self.navigationController rootViewController];

编辑

OP 请求 cmets 中的属性。

如果您愿意,您可以通过 self.navigationController.rootViewController 之类的方式访问它,只需在您的标题中添加一个只读属性即可:

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

【讨论】:

    【解决方案4】:

    对于所有对快速扩展感兴趣的人,这就是我现在正在使用的:

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

    【讨论】:

    • 谢谢!你也可以删除“as?UIViewController”
    【解决方案5】:

    作为@dulgan's 答案的补充,使用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”比 [0] 更好
    【解决方案6】:

    UIApplicationsingleton 询问其keyWindow 怎么样,然后从UIWindow 询问根视图控制器(其rootViewController 属性):

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

    【讨论】:

    • 如何获取导航控制器?
    • 这是一个错误的建议,如果我的应用程序的 rootviewcontroller 是 UITabBarViewController?
    【解决方案7】:

    在这里我想出了从任何地方导航到根目录的通用方法。

    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)
          ...
      }
      

    【讨论】:

      【解决方案8】:

      我遇到了一个奇怪的情况。

      self.viewControllers.first 始终不是根 viewController。

      一般来说,self.viewControllers.first 确实是根 viewController。但有时并非如此。

      class MyCustomMainNavigationController: UINavigationController {
      
          function configureForView(_ v: UIViewController, animated: Bool) {
              let root = self.viewControllers.first
              let isRoot = (v == root)
          
              // Update UI based on isRoot
              // ....
          }
      }
      
      extension MyCustomMainNavigationController: UINavigationControllerDelegate {
          func navigationController(_ navigationController: UINavigationController, 
              willShow viewController: UIViewController, 
              animated: Bool) {
      
              self.configureForView(viewController, animated: animated)
          }
      }
      

      我的问题:

      通常,self.viewControllers.firstroot viewController。 但是,当我调用popToRootViewController(animated:) 时,它会触发navigationController(_:willShow:animated:)。此时self.viewControllers.first不是根viewController,它是最后一个会消失的viewController。

      总结

      • self.viewControllers.first 并不总是 root viewController。有时,它会是最后一个 viewController。

      所以,当self.viewControllers 只有一个视图控制器时,我建议按属性保留rootViewController。我在自定义 UINavigationController 的 viewDidLoad() 中获得根 viewController。

      class MyCustomMainNavigationController: UINavigationController {
          fileprivate var myRoot: UIViewController!
          override func viewDidLoad() {
      
              super.viewDidLoad()
      
              // My UINavigationController is defined in storyboard. 
              // So at this moment, 
              // I can get root viewController by `self.topViewController!`
              let v = self.topViewController!
              self.myRoot = v
          }
      
      }
      

      环境:

      • 装有 iOS 14.0.1 的 iPhone 7
      • Xcode 12.0.1 (12A7300)

      【讨论】:

      • 我也看到了,旧的遗留代码失败了~!!!
      【解决方案9】:

      这对我有用:

      当我的根视图控制器嵌入导航控制器时:

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

      请记住,keyWindow 已弃用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多