Zev Eisenberg's answer is simple and straightforward, but it does not always work, and it may fail with this warning message:
Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10>
on <ThisViewController: 0x7fe6fb409480> which is already presenting
<AnotherViewController: 0x7fe6fd109c00>
This is because the windows rootViewController is not at the top of the presented views. To correct this we need to walk up the presentation chain, as shown in my UIAlertController extension code written in Swift 3:
/// show the alert in a view controller if specified; otherwise show from window's root pree
func show(inViewController: UIViewController?) {
if let vc = inViewController {
vc.present(self, animated: true, completion: nil)
} else {
// find the root, then walk up the chain
var viewController = UIApplication.shared.keyWindow?.rootViewController
var presentedVC = viewController?.presentedViewController
while presentedVC != nil {
viewController = presentedVC
presentedVC = viewController?.presentedViewController
}
// now we present
viewController?.present(self, animated: true, completion: nil)
}
}
func show() {
show(inViewController: nil)
}
Updates on 9/15/2017:
Tested and confirmed that the above logic still works great in the newly available iOS 11 GM seed. The top voted method by agilityvision, however, does not: the alert view presented in a newly minted UIWindow
is below the keyboard and potentially prevents the user from tapping its buttons. This is because in iOS 11 all windowLevels higher than that of keyboard window is lowered to a level below it.
One artifact of presenting from keyWindow
though is the animation of keyboard sliding down when alert is presented, and sliding up again when alert is dismissed. If you want the keyboard to stay there during presentation, you can try to present from the top window itself, as shown in below code:
func show(inViewController: UIViewController?) {
if let vc = inViewController {
vc.present(self, animated: true, completion: nil)
} else {
// get a "solid" window with the highest level
let alertWindow = UIApplication.shared.windows.filter { $0.tintColor != nil || $0.className() == "UIRemoteKeyboardWindow" }.sorted(by: { (w1, w2) -> Bool in
return w1.windowLevel < w2.windowLevel
}).last
// save the top window's tint color
let savedTintColor = alertWindow?.tintColor
alertWindow?.tintColor = UIApplication.shared.keyWindow?.tintColor
// walk up the presentation tree
var viewController = alertWindow?.rootViewController
while viewController?.presentedViewController != nil {
viewController = viewController?.presentedViewController
}
viewController?.present(self, animated: true, completion: nil)
// restore the top window's tint color
if let tintColor = savedTintColor {
alertWindow?.tintColor = tintColor
}
}
}
The only not so great part of the above code is that it checks the class name UIRemoteKeyboardWindow
to make sure we can include it too. Nevertheless the above code does work great in iOS 9, 10 and 11 GM seed, with the right tint color and without the keyboard sliding artifacts.