【问题标题】:Get current view controller from the app delegate (modal is possible)从应用程序委托获取当前视图控制器(模式是可能的)
【发布时间】:2013-12-10 03:39:24
【问题描述】:

我知道要从应用委托中获取当前视图控制器,我可以使用为我的应用设置的navigationController 属性。但是,在我的应用程序中的许多地方都可能出现了模态导航控制器。有什么方法可以从应用代理中检测到这一点,因为当前的导航控制器将不同于应用代理所引用的导航控制器?

【问题讨论】:

  • 具体来说,我想从- (void)applicationDidEnterBackground:(UIApplication *)application 检测当前视图控制器

标签: ios objective-c delegates appdelegate


【解决方案1】:

基于gist here,我创建了一个类别来获取最顶层的视图控制器,这样调用[[UIApplication sharedApplication] topMostViewController] 将为您提供应用中最顶层的视图控制器。

这在 iOS 8 中特别有用,其中 UIAlertViewUIActionSheet 已被弃用,取而代之的是 UIAlertController,它需要呈现在最顶层的视图控制器上。

UIViewController+TopMostViewController.h

#import <UIKit/UIKit.h>

@interface UIViewController (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

@interface UIApplication (TopMostViewController)

- (UIViewController *)topMostViewController;

@end

UIViewController+TopMostViewController.m

#import "UIViewController+TopMostViewController.h"

@implementation UIViewController (TopMostViewController)

- (UIViewController *)topMostViewController
{
    if (self.presentedViewController == nil)
    {
        return self;
    }
    else if ([self.presentedViewController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)self.presentedViewController;
        UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
        return [lastViewController topMostViewController];
    }

    UIViewController *presentedViewController = (UIViewController *)self.presentedViewController;
    return [presentedViewController topMostViewController];
}

@end

#pragma mark -

@implementation UIApplication (TopMostViewController)

- (UIViewController *)topMostViewController
{
    return [self.keyWindow.rootViewController topMostViewController];
}

@end

【讨论】:

  • 我认为你需要切换 .h 和 .m :)
  • 很棒的答案,我相信新的语法已经改变了,topMostViewController 至少对于 Swift 2 不再是一个属性。
【解决方案2】:

我建议你使用 NSNofiticationCenter。

//in AppDelegate:
@interface AppDelegate()
{
    ...
    id lastViewController;
    ...
}

@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleCurrentViewController) name:@"CurrentViewController" object:nil];
    ...
}

- (void)handleCurrentViewController:(NSNotification *)notification {
    if([[notification userInfo] objectForKey:@"lastViewController"]) {
        lastViewController = [[notification userInfo] objectForKey:@"lastViewController"];
    }
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{    
    NSLog(@"last view controller is %@", [(UIViewController *)lastViewController class]);
}
@end

//in every ViewController you want to detect
@implementation SomeViewController
...
- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"CurrentViewController" object:nil userInfo:[NSDictionary dictionaryWithObjectsAndKeys:self, @"lastViewController", nil]];
}
...
@end

【讨论】:

  • 这很棒。谢谢!
  • 我爱你!我现在一直被这个问题困扰,你的解决方案解决了它!谢谢!
  • 您按下主页按钮或锁定设备,viewWillDisappear 将永远不会被调用。它直接调用applicationDidEnterBackground。否则,您可以轻松地将最后一个活动视图保存在 NSUserDefaults 中。
  • 我所做的是,每次在其 viewWillAppear 的 NSUserDefaults 字段中保存最后一个 viewController 名称。这样我就可以访问用户在整个应用程序中使用的最后一个活动视图。
  • @Maziyar 我认为applicationWillResignActiveapplicationDidEnterBackground 之前被调用。我喜欢你的想法并将使用它!
【解决方案3】:

Swift 中的优秀解决方案,在 AppDelegate 中实现

func getTopViewController()->UIViewController{
    return topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
}
func topViewControllerWithRootViewController(rootViewController:UIViewController)->UIViewController{
    if rootViewController is UITabBarController{
        let tabBarController = rootViewController as! UITabBarController
        return topViewControllerWithRootViewController(tabBarController.selectedViewController!)
    }
    if rootViewController is UINavigationController{
        let navBarController = rootViewController as! UINavigationController
        return topViewControllerWithRootViewController(navBarController.visibleViewController)
    }
    if let presentedViewController = rootViewController.presentedViewController {
        return topViewControllerWithRootViewController(presentedViewController)
    }
    return rootViewController
}

目标 - C

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
       UITabBarController* tabBarController = (UITabBarController*)rootViewController;
       return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
      UINavigationController* navigationController = (UINavigationController*)rootViewController;
      return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
     } else if (rootViewController.presentedViewController) {
       UIViewController* presentedViewController = rootViewController.presentedViewController;
       return [self topViewControllerWithRootViewController:presentedViewController];
     } else {
       return rootViewController;
    }
}

【讨论】:

  • 如何处理UISplitViewController ?暂时不能测试,怕是不行??
【解决方案4】:

这对我有用。我有许多具有不同控制器的目标,因此以前的答案似乎不起作用。

首先你想在你的 AppDelegate 类中使用这个:

var window: UIWindow?

然后,在你的函数中

let navigationController = window?.rootViewController as? UINavigationController
if let activeController = navigationController!.visibleViewController {
    if activeController.isKindOfClass( MyViewController )  {
        println("I have found my controller!")    
   }
}

【讨论】:

  • 当我在其他文件的函数中编写此代码时,它在第一行 let navi.... 给我错误 use of unresolved identifire 'window'
  • Varun Naharia,听起来函数在 AppDelegate 类之外。如果你把“var window”和函数放在里面,你不应该得到那个错误。
  • 感谢您的回复,但我现在解决了问题,我的问题是 stackoverflow.com/questions/30174695/…,但一些 stackoverflow 用户决定关闭它:D,谢谢
  • 干得好!但是我发现你可以简单地调用 self.window?.rootViewController,你不需要在顶部有 window 变量。
  • @Unome,谢谢,是的,你是对的。由于其他原因,我在整个 AppDelegate 中使用 window 变量,所以我把它扔在那里。
【解决方案5】:

您可以尝试通过以下方式获得最高的视图

[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

虽然这个视图可能是不可见的,甚至被它的一些子视图覆盖...

这取决于您的用户界面,但它可能会有所帮助。

【讨论】:

    【解决方案6】:

    如果您在 App Delegate 中有导航控制器,只需使用 visibleViewController 属性。它会给你可见的控制器,即使它是一个模态的。

    【讨论】:

    • 你能准确地说“如果你有导航控制器”吗?我们是否需要在 appDelegate 中创建 UINavigationController 的实例,并使用 .visibleViewController 访问 visibleViewController,如下所示:var navVC = UINavigationController() 然后 navVC.visibleViewController?
    【解决方案7】:

    在 swift 中,您可以像这样获得活动的 ViewController:

     let navigationController = application.windows[0].rootViewController as UINavigationController
    
     let activeViewCont = navigationController.visibleViewController
    

    【讨论】:

      【解决方案8】:

      上述其他解决方案仅部分起作用,因为未处理复杂的视图层次结构(导航控制器集成在选项卡栏控制器、拆分视图控制器、视图容器和警报控制器中可能会搞砸)。

      我通过保留当前视图控制器的引用来解决这个问题 应用委托。每次出现视图时,我都会利用 viewDidAppear(animated:) 并在应用委托中设置引用。

      我知道这个问题是关于 Objective-C 的,但我只能提供 Swift 代码,我相信它对这两种类型的用户都有用。

      首先:我已经实现了一个协议来保持干净和可重复使用:

      protocol UpdatableViewController {
          func updateUI()
      }
      

      第二:我添加了对 AppDelegate 的引用:

      var currentViewController: UpdatableViewController?
      

      第三:我在viewDidAppear()中设置了当前视图控制器:

      override func viewDidAppear(animated: Bool) {
          super.viewDidAppear(animated)
          let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
          appDelegate?.currentViewController = self
      }
      

      第四:

      extension ViewController1: UpdatableViewController {
          func updateUI() {
              print("Implement updating here")
          }
      }
      

      【讨论】:

        【解决方案9】:

        最好的解决方案,也适用于UITabBarController中的moreNavigationController

        extension UIApplication {
            class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {
        
                if let nav = base as? UINavigationController {
                    return topViewController(nav.visibleViewController)
                }
        
                if let tab = base as? UITabBarController {
                    let moreNavigationController = tab.moreNavigationController
        
                    if let top = moreNavigationController.topViewController  where top.view.window != nil {
                        return topViewController(top)
                    } else if let selected = tab.selectedViewController {
                        return topViewController(selected)
                    }
                }
        
                if let presented = base?.presentedViewController {
                    return topViewController(presented)
                }
        
                return base
            }
        }
        

        【讨论】:

          【解决方案10】:

          下面的代码效果很好。

          +(UIViewController*) findBestViewController:(UIViewController*)vc {

          if (vc.presentedViewController) {
          
              // Return presented view controller
              return [AppDelegate findBestViewController:vc.presentedViewController];
          
          } else if ([vc isKindOfClass:[UISplitViewController class]]) {
          
              // Return right hand side
              UISplitViewController* svc = (UISplitViewController*) vc;
              if (svc.viewControllers.count > 0)
                  return [AppDelegate findBestViewController:svc.viewControllers.lastObject];
              else
                  return vc;
          
          } else if ([vc isKindOfClass:[UINavigationController class]]) {
          
              // Return top view
              UINavigationController* svc = (UINavigationController*) vc;
              if (svc.viewControllers.count > 0)
                  return [AppDelegate findBestViewController:svc.topViewController];
              else
                  return vc;
          
          } else if ([vc isKindOfClass:[UITabBarController class]]) {
          
              // Return visible view
              UITabBarController* svc = (UITabBarController*) vc;
              if (svc.viewControllers.count > 0)
                  return [AppDelegate findBestViewController:svc.selectedViewController];
              else
                  return vc;
          
          } else {
          
              // Unknown view controller type, return last child view controller
              return vc;
          
          }
          

          }

          +(UIViewController*) currentViewController {

          // Find best view controller
          UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
          return [AppDelegate findBestViewController:viewController];
          

          }

          【讨论】:

            猜你喜欢
            • 2014-09-09
            • 2012-08-09
            • 2021-05-30
            • 1970-01-01
            • 2015-08-22
            • 2011-12-29
            • 2011-12-27
            • 2013-10-29
            • 2020-09-20
            相关资源
            最近更新 更多