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

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

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

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


当前回答

其中一些答案只对我起了部分作用,将它们组合在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 ...

其他回答

你可以尝试在UIViewController上用一个方法来实现一个类别 - (void)presentErrorMessage;,在这个方法中你实现了UIAlertController,然后在self上呈现它。然后在你的客户端代码中,你会有这样的东西:

[myViewController presentErrorMessage];

这样可以避免不必要的参数和关于视图不在窗口层次结构中的警告。

斯威夫特5

在显示消息后隐藏窗口是很重要的。

func showErrorMessage(_ message: String) {
    let alertWindow = UIWindow(frame: UIScreen.main.bounds)
    alertWindow.rootViewController = UIViewController()

    let alertController = UIAlertController(title: "Error", message: message, preferredStyle: UIAlertController.Style.alert)
    alertController.addAction(UIAlertAction(title: "Close", style: UIAlertAction.Style.cancel, handler: { _ in
        alertWindow.isHidden = true
    }))
    
    alertWindow.windowLevel = UIWindow.Level.alert + 1;
    alertWindow.makeKeyAndVisible()
    alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)
}

对于iOS 13,基于mythicalcoder和bobbyrehm的回答:

在iOS 13中,如果你正在创建自己的窗口来显示警报,你需要保持对该窗口的强引用,否则你的警报将不会显示,因为当它的引用退出作用域时,窗口将立即被释放。

此外,在警报解除后,您需要再次将引用设置为nil,以便删除窗口,继续允许用户在它下面的主窗口上进行交互。

你可以创建一个UIViewController子类来封装窗口内存管理逻辑:

class WindowAlertPresentationController: UIViewController {

    // MARK: - Properties

    private lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
    private let alert: UIAlertController

    // MARK: - Initialization

    init(alert: UIAlertController) {

        self.alert = alert
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {

        fatalError("This initializer is not supported")
    }

    // MARK: - Presentation

    func present(animated: Bool, completion: (() -> Void)?) {

        window?.rootViewController = self
        window?.windowLevel = UIWindow.Level.alert + 1
        window?.makeKeyAndVisible()
        present(alert, animated: animated, completion: completion)
    }

    // MARK: - Overrides

    override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {

        super.dismiss(animated: flag) {
            self.window = nil
            completion?()
        }
    }
}

你可以这样使用它,或者如果你想在你的UIAlertController上使用一个方便的方法,你可以把它扔到一个扩展中:

extension UIAlertController {

    func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {

        let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
        windowAlertPresentationController.present(animated: animated, completion: completion)
    }
}

你可以用Swift 2.2做以下事情:

let alertController: UIAlertController = ...
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)

Swift 3.0:

let alertController: UIAlertController = ...
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)

斯威夫特5

我刚刚创建了一个新窗口,并在其中添加了警报视图控制器。

查看类TopViewController:

https://gist.github.com/odnaks/3f3fd0d20f318c6276e76d0f9d7de5a7

我使用它很简单,像UIAlertController:

 let alert = TopAlertController()
 alert.title = "title"
 alert.message = "message"
 alert.addAction(UIAlertAction(title: "Ок", style: .default, handler: { _ in }))
 alert.show()