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

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

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

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


当前回答

对于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)
          }
        }
    }
}

其他回答

在Objective-C中显示警报的简单方法:

[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];

alertController是你的UIAlertController对象。

注意:你还需要确保你的助手类扩展了UIViewController

你可以使用两种方法:

-使用UIAlertView或'UIActionSheet'代替(不推荐,因为它在iOS 8中已弃用,但现在可以使用了)

-记得上次显示的视图控制器。举个例子。

@interface UIViewController (TopController)
+ (UIViewController *)topViewController;
@end

// implementation

#import "UIViewController+TopController.h"
#import <objc/runtime.h>

static __weak UIViewController *_topViewController = nil;

@implementation UIViewController (TopController)

+ (UIViewController *)topViewController {
    UIViewController *vc = _topViewController;
    while (vc.parentViewController) {
        vc = vc.parentViewController;
    }
    return vc;
}

+ (void)load {
    [super load];
    [self swizzleSelector:@selector(viewDidAppear:) withSelector:@selector(myViewDidAppear:)];
    [self swizzleSelector:@selector(viewWillDisappear:) withSelector:@selector(myViewWillDisappear:)];
}

- (void)myViewDidAppear:(BOOL)animated {
    if (_topViewController == nil) {
        _topViewController = self;
    }

    [self myViewDidAppear:animated];
}

- (void)myViewWillDisappear:(BOOL)animated {
    if (_topViewController == self) {
        _topViewController = nil;
    }

    [self myViewWillDisappear:animated];
}

+ (void)swizzleSelector:(SEL)sel1 withSelector:(SEL)sel2
{
    Class class = [self class];

    Method originalMethod = class_getInstanceMethod(class, sel1);
    Method swizzledMethod = class_getInstanceMethod(class, sel2);

    BOOL didAddMethod = class_addMethod(class,
                                        sel1,
                                        method_getImplementation(swizzledMethod),
                                        method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
                            sel2,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

@end 

用法:

[[UIViewController topViewController] presentViewController:alertController ...];

创建helper类AlertWindow并使用as

let alertWindow = AlertWindow();
let alert = UIAlertController(title: "Hello", message: "message", preferredStyle: .alert);
let cancel = UIAlertAction(title: "Ok", style: .cancel){(action) in

    //....  action code here

    // reference to alertWindow retain it. Every action must have this at end

    alertWindow.isHidden = true;

   //  here AlertWindow.deinit{  }

}
alert.addAction(cancel);
alertWindow.present(alert, animated: true, completion: nil)


class AlertWindow:UIWindow{

    convenience init(){
        self.init(frame:UIScreen.main.bounds);
    }

    override init(frame: CGRect) {
        super.init(frame: frame);
        if let color = UIApplication.shared.delegate?.window??.tintColor {
            tintColor = color;
        }
        rootViewController = UIViewController()
        windowLevel = UIWindowLevelAlert + 1;
        makeKeyAndVisible()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    deinit{
        //  semaphor.signal();
    }

    func present(_ ctrl:UIViewController, animated:Bool, completion: (()->Void)?){
        rootViewController!.present(ctrl, animated: animated, completion: completion);
    }
}

更新到与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中适用于普通的视图控制器,即使屏幕上有一个导航控制器:

let alert = UIAlertController(...)

let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.presentViewController(alert, animated: true, completion: nil)