A resposta de Zev Eisenberg é simples e direta, mas nem sempre funciona, e pode falhar com esta mensagem de aviso:
Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10>
on <ThisViewController: 0x7fe6fb409480> which is already presenting
<AnotherViewController: 0x7fe6fd109c00>
Isso ocorre porque o rootViewController do Windows não está no topo das visualizações apresentadas. Para corrigir isso, precisamos percorrer a cadeia de apresentação, conforme mostrado no meu código de extensão UIAlertController escrito em 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)
}
Atualizações em 15/09/2017:
Testou e confirmou que a lógica acima ainda funciona muito bem na nova semente iOS 11 GM disponível. O método mais votado pela agilityvision, no entanto, não: a exibição de alerta apresentada em umUIWindow
fica abaixo do teclado e potencialmente impede que o usuário toque em seus botões. Isso ocorre porque no iOS 11 todos os níveis da janela mais altos que os da janela do teclado são reduzidos para um nível abaixo dele.
Um artefato de apresentação do keyWindow
porém é a animação do teclado deslizando para baixo quando o alerta é apresentado e deslizando novamente para cima quando o alerta é descartado. Se você deseja que o teclado permaneça lá durante a apresentação, tente apresentar a partir da janela superior, como mostra o código abaixo:
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
}
}
}
A única parte não tão boa do código acima é que ele verifica o nome da classe UIRemoteKeyboardWindow
para garantir que também possamos incluí-lo. No entanto, o código acima funciona muito bem nas sementes GM iOS 9, 10 e 11 GM, com a cor certa e sem os artefatos deslizantes do teclado.