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

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

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

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


当前回答

我在我的AppDelegate类中使用了这段代码和一些个人变化

-(UIViewController*)presentingRootViewController
{
    UIViewController *vc = self.window.rootViewController;
    if ([vc isKindOfClass:[UINavigationController class]] ||
        [vc isKindOfClass:[UITabBarController class]])
    {
        // filter nav controller
        vc = [AppDelegate findChildThatIsNotNavController:vc];
        // filter tab controller
        if ([vc isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tbc = ((UITabBarController*)vc);
            if ([tbc viewControllers].count > 0) {
                vc = [tbc viewControllers][tbc.selectedIndex];
                // filter nav controller again
                vc = [AppDelegate findChildThatIsNotNavController:vc];
            }
        }
    }
    return vc;
}
/**
 *   Private helper
 */
+(UIViewController*)findChildThatIsNotNavController:(UIViewController*)vc
{
    if ([vc isKindOfClass:[UINavigationController class]]) {
        if (((UINavigationController *)vc).viewControllers.count > 0) {
            vc = [((UINavigationController *)vc).viewControllers objectAtIndex:0];
        }
    }
    return vc;
}

其他回答

更新到与iOS 13场景,打破了新的UIWindow方法。斯威夫特5.1。

fileprivate var alertWindows = [UIAlertController:UIWindow]()

extension UIAlertController {

    func presentInNewWindow(animated: Bool, completion: (() -> Void)?) {
        let foregroundActiveScene = UIApplication.shared.connectedScenes.filter { $0.activationState == .foregroundActive }.first
        guard let foregroundWindowScene = foregroundActiveScene as? UIWindowScene else { return }

        let window = UIWindow(windowScene: foregroundWindowScene)
        alertWindows[self] = window

        window.rootViewController = UIViewController()
        window.windowLevel = .alert + 1
        window.makeKeyAndVisible()
        window.rootViewController!.present( self, animated: animated, completion: completion)
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        alertWindows[self] = nil
    }

}

如果有人感兴趣,我创建了一个Swift 3版本的@agilityvision答案。代码:

import Foundation
import UIKit

extension UIAlertController {

    var window: UIWindow? {
        get {
            return objc_getAssociatedObject(self, "window") as? UIWindow
        }
        set {
            objc_setAssociatedObject(self, "window", newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.window?.isHidden = true
        self.window = nil
    }

    func show(animated: Bool = true) {
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = UIViewController(nibName: nil, bundle: nil)

        let delegate = UIApplication.shared.delegate
        if delegate?.window != nil {
            window.tintColor = delegate!.window!!.tintColor
        }

        window.windowLevel = UIApplication.shared.windows.last!.windowLevel + 1

        window.makeKeyAndVisible()
        window.rootViewController!.present(self, animated: animated, completion: nil)

        self.window = window
    }
}

我尝试了上面提到的所有方法,但都没有成功。我在Swift 3.0中使用的方法:

extension UIAlertController {
    func show() {
        present(animated: true, completion: nil)
    }

    func present(animated: Bool, completion: (() -> Void)?) {
        if var topController = UIApplication.shared.keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }
            topController.present(self, animated: animated, completion: completion)
        }
    }
}

iOS13场景支持(当使用UIWindowScene时)

import UIKit

private var windows: [String:UIWindow] = [:]

extension UIWindowScene {
    static var focused: UIWindowScene? {
        return UIApplication.shared.connectedScenes
            .first { $0.activationState == .foregroundActive && $0 is UIWindowScene } as? UIWindowScene
    }
}

class StyledAlertController: UIAlertController {

    var wid: String?

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

        //let window = UIWindow(frame: UIScreen.main.bounds)
        guard let window = UIWindowScene.focused.map(UIWindow.init(windowScene:)) else {
            return
        }
        window.rootViewController = UIViewController()
        window.windowLevel = .alert + 1
        window.makeKeyAndVisible()
        window.rootViewController!.present(self, animated: animated, completion: completion)

        wid = UUID().uuidString
        windows[wid!] = window
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if let wid = wid {
            windows[wid] = nil
        }

    }

}

对于UINavigationController和/或UITabBarController的所有情况,非常通用的UIAlertController扩展。如果屏幕上有一个模态VC,也可以工作。

用法:

//option 1:
myAlertController.show()
//option 2:
myAlertController.present(animated: true) {
    //completion code...
}

这是扩展:

//Uses Swift1.2 syntax with the new if-let
// so it won't compile on a lower version.
extension UIAlertController {

    func show() {
        present(animated: true, completion: nil)
    }

    func present(#animated: Bool, completion: (() -> Void)?) {
        if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController {
            presentFromController(rootVC, animated: animated, completion: completion)
        }
    }

    private func presentFromController(controller: UIViewController, animated: Bool, completion: (() -> Void)?) {
        if  let navVC = controller as? UINavigationController,
            let visibleVC = navVC.visibleViewController {
                presentFromController(visibleVC, animated: animated, completion: completion)
        } else {
          if  let tabVC = controller as? UITabBarController,
              let selectedVC = tabVC.selectedViewController {
                presentFromController(selectedVC, animated: animated, completion: completion)
          } else {
              controller.presentViewController(self, animated: animated, completion: completion)
          }
        }
    }
}