22

有没有办法在不知道可见视图控制器视图是什么的情况下以模态方式呈现视图控制器?基本上有点像您会在任何时间点显示警报视图。

我希望能够做类似的事情:

MyViewController *myVC = [[MyViewController alloc] init];
[myVC showModally];

我希望能够从应用程序中的任何位置调用它,并将其显示在顶部。我不想关心当前的视图控制器是什么。

我打算用它来显示登录提示。我不想使用警报视图,也不想在整个应用程序中使用登录演示代码。

对此有什么想法吗?还是有更好的方法来实现这一目标?我应该只实现自己的机制并在窗口顶部放置一个视图吗?

4

5 回答 5

31

好吧,你可以跟随链条。

开始于[UIApplication sharedApplication].delegate.window.rootViewController

在每个视图控制器上执行以下一系列测试。

如果[viewController isKindOfClass:[UINavigationController class]],则继续[(UINavigationController *)viewController topViewController]

如果[viewController isKindOfClass:[UITabBarController class]],则继续[(UITabBarController *)viewController selectedViewController]

如果[viewController presentedViewController],则继续[viewController presentedViewController]

于 2013-04-12T01:25:39.607 回答
23

我在 Swift 中的解决方案(灵感来自 MartinMoizard 的要点)

extension UIViewController {
    func presentViewControllerFromVisibleViewController(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
        if let navigationController = self as? UINavigationController {
            navigationController.topViewController?.presentViewControllerFromVisibleViewController(viewControllerToPresent, animated: flag, completion: completion)
        } else if let tabBarController = self as? UITabBarController {
            tabBarController.selectedViewController?.presentViewControllerFromVisibleViewController(viewControllerToPresent, animated: flag, completion: completion)
        } else if let presentedViewController = presentedViewController {
            presentedViewController.presentViewControllerFromVisibleViewController(viewControllerToPresent, animated: flag, completion: completion)
        } else {
            present(viewControllerToPresent, animated: flag, completion: completion)
        }
    }
}
于 2015-03-10T13:39:35.623 回答
14

该解决方案为您提供了最顶级的视图控制器,以便您可以在呈现之前处理任何特殊情况。例如,也许您只想在最顶层的视图控制器不是特定的视图控制器时才显示您的视图控制器。

extension UIApplication {
    /// The top most view controller
    static var topMostViewController: UIViewController? {
        return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
    }
}

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else {
            return self
        }
    }
}

有了这个,您可以从任何地方展示您的视图控制器,而无需知道最顶层的视图控制器是什么

UIApplication.topMostViewController?.present(viewController, animated: true, completion: nil)

或者仅当最顶层的视图控制器不是特定的视图控制器时才显示您的视图控制器

if let topVC = UIApplication.topMostViewController, !(topVC is FullScreenAlertVC) {
    topVC.present(viewController, animated: true, completion: nil)
}

需要注意的一点是,如果当前正在显示 UIAlertController,UIApplication.topMostViewController将返回一个UIAlertController. 在 a 之上呈现UIAlertController有奇怪的行为,应该避免。因此,您应该!(UIApplication.topMostViewController is UIAlertController)在呈现之前手动检查,或者添加一个else if案例以返回 nil ifself is UIAlertController

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else if self is UIAlertController {
            return nil
        } else {
            return self
        }
    }
}
于 2017-08-03T00:53:51.850 回答
7

您可以在您的应用委托中实现此代码:

AppDelegate.m

-(void)presentViewControllerFromVisibleController:(UIViewController *)toPresent
{
    UIViewController *vc = self.window.rootViewController;
    [vc presentViewController:toPresent animated:YES];
}

AppDelegate.h

-(void)presentViewControllerFromVisibleViewController:(UIViewController *)toPresent;

从哪里

#import "AppDelegate.h"
...
AppDelegate *delegate = [UIApplication sharedApplication].delegate;
[delegate presentViewControllerFromVisibleViewController:myViewControllerToPresent];

在您的委托中,您将rootViewController获得window. 这将始终可见-它是所有事物的“父”控制器。

于 2013-04-12T01:17:43.293 回答
2

我认为您不一定需要知道哪个视图控制器可见。您可以访问keyWindow应用程序并将模态视图控制器的视图添加到视图列表的顶部。然后你可以让它像UIAlertView.

接口文件: MyModalViewController.h

#import <UIKit/UIKit.h>

@interface MyModalViewController : UIViewController
- (void) show;
@end

实现文件: MyModalViewController.m

#import "MyModalViewController.h"


@implementation MyModalViewController

- (void) show {
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    //  Configure the frame of your modal's view.
    [window addSubview: self.view];
}

@end
于 2013-04-12T01:43:59.480 回答