La respuesta de Zev Eisenberg es simple y directa, pero no siempre funciona, y puede fallar con este mensaje de advertencia:
Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10>
on <ThisViewController: 0x7fe6fb409480> which is already presenting
<AnotherViewController: 0x7fe6fd109c00>
Esto se debe a que Windows rootViewController no está en la parte superior de las vistas presentadas. Para corregir esto, necesitamos avanzar por la cadena de presentación, como se muestra en mi código de extensión UIAlertController escrito en 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)
}
Actualizaciones el 15/09/2017:
Probado y confirmado que la lógica anterior todavía funciona muy bien en la semilla GM iOS 11 recién disponible. Sin embargo, el método más votado por agilityvision no lo hace: la vista de alerta presentada en una nueva acuñada UIWindow
está debajo del teclado y potencialmente evita que el usuario toque sus botones. Esto se debe a que en iOS 11 todos los niveles de ventana superiores a los de la ventana del teclado se reducen a un nivel inferior.
Sin embargo, un artefacto de presentación keyWindow
es la animación del teclado deslizándose hacia abajo cuando se presenta la alerta, y deslizándose hacia arriba nuevamente cuando se desactiva la alerta. Si desea que el teclado permanezca allí durante la presentación, puede intentar presentar desde la ventana superior, como se muestra en el siguiente código:
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
}
}
}
La única parte no tan buena del código anterior es que verifica el nombre de la clase UIRemoteKeyboardWindow
para asegurarse de que podamos incluirlo también. Sin embargo, el código anterior funciona muy bien en iOS 9, 10 y 11 semillas GM, con el color de tinte correcto y sin los artefactos deslizantes del teclado.