场景:用户点击视图控制器上的按钮。视图控制器是导航堆栈中最顶层的(很明显)。tap调用在另一个类上调用的实用程序类方法。这里发生了不好的事情我想在控件返回到视图控制器之前在那里显示一个警告。

+ (void)myUtilityMethod {
    // do stuff
    // something bad happened, display an alert.
}

这是可能的UIAlertView(但可能不太合适)。

在这种情况下,你如何在myUtilityMethod中呈现UIAlertController ?


当前回答

我知道这是针对iOS的,因为搜索引擎中几乎所有的链接都能找到iOS的帖子,我想我应该把这个提供给macOS的开发者。

macOS上的Swift 5.5

根据Darkngs的回答,我在另一个类中添加了这个方法:

let alert = NSAlert()
let viewController = NSApplication.shared.keyWindow?.contentViewController
alert.messageText = "An Alert Message."
alert.addButton(withTitle: "Ok")
alert.beginSheetModal(for: (viewController?.view.window)!) {
    (returnCode: NSApplication.ModalResponse) -> Void in
}

其他回答

另一个选择:

    var topController:UIViewController = UIApplication.shared.keyWindow!.rootViewController!
    while ((topController.presentedViewController) != nil) {
        topController = topController.presentedViewController!
    }
    topController.present(alert, animated:true, completion:nil)

Swift 4+ / iOS 13:

基于https://stackoverflow.com/a/47797463/7493938,它使用了iOS 13已弃用的。keywindow方法。

extension UIAlertController {
  func show(animated: Bool = true, completion: (() -> Void)? = nil) {
    if let visibleViewController = UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.visibleViewController {
      visibleViewController.present(self, animated: animated, completion: completion)
    }
  }
}

(我无法编辑原始答案,因为编辑队列已满。

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)

需要注意的一件事是,如果有一个UIAlertController当前正在显示,UIApplication。topMostViewController将返回一个UIAlertController。在UIAlertController的顶部呈现有奇怪的行为,应该避免。因此,你应该手动检查!topMostViewController is UIAlertController),或者如果self是UIAlertController,添加一个else if case返回nil

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

似乎有效:

static UIViewController *viewControllerForView(UIView *view) {
    UIResponder *responder = view;
    do {
        responder = [responder nextResponder];
    }
    while (responder && ![responder isKindOfClass:[UIViewController class]]);
    return (UIViewController *)responder;
}

-(void)showActionSheet {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Do it" style:UIAlertActionStyleDefault handler:nil]];
    [viewControllerForView(self) presentViewController:alertController animated:YES completion:nil];
}

其中一些答案只对我起了部分作用,将它们组合在AppDelegate中的以下类方法中是我的解决方案。它在iPad上工作,在UITabBarController视图中,在UINavigationController中,在呈现情态时。在iOS 10和13上进行测试。

+ (UIViewController *)rootViewController {
    UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
    if([rootViewController isKindOfClass:[UINavigationController class]])
        rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
    if([rootViewController isKindOfClass:[UITabBarController class]])
        rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
    if (rootViewController.presentedViewController != nil)
        rootViewController = rootViewController.presentedViewController;
    return rootViewController;
}

用法:

[[AppDelegate rootViewController] presentViewController ...