Qual é a melhor maneira de verificar se um UIAlertController já está apresentando?

109

Eu tenho um tableview que, quando carregado, cada célula pode retornar um NSError, que escolhi exibir em um UIAlertController. O problema é que recebo esse erro no console se vários erros forem retornados.

Aviso: tentativa de apresentar UIAlertController: 0x14e64cb00 em MessagesMasterVC: 0x14e53d800 que já está apresentando (nulo)

Idealmente, eu gostaria de lidar com isso em meu método de extensão UIAlertController.

class func simpleAlertWithMessage(message: String!) -> UIAlertController {

    let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
    let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)

    alertController.addAction(cancel)
    return alertController
}

Com base na resposta de matt, mudei a extensão para uma extensão UIViewController, é muito mais limpa e salva muito código presentViewController.

    func showSimpleAlertWithMessage(message: String!) {

    let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
    let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)

    alertController.addAction(cancel)

    if self.presentedViewController == nil {
        self.presentViewController(alertController, animated: true, completion: nil)
    }
}
oculto-nome de usuário
fonte
Obrigado por postar seu código atualizado.
djbp de
Eu também movi o resto do código (três linhas para configurar o UIAlertController) para a instrução If, porque ainda estava dando o seguinte erro (Tentar carregar a visão de um controlador de visão enquanto ele está desalocando não é permitido e pode resultar em comportamento indefinido)
Kitson
Gostaria de referir a solução no link abaixo, verifique stackoverflow.com/a/39994115/1872233
iDevAmit

Respostas:

119

Não é o UIAlertController que "já está apresentando", é o MessagesMasterVC. Um controlador de visualização pode apresentar apenas um outro controlador de visualização por vez. Daí a mensagem de erro.

Em outras palavras, se você disse a um controlador de visualização para presentViewController:...fazer isso, você não pode fazer isso novamente até que o controlador de visualização apresentado seja dispensado.

Você pode perguntar ao MessagesMasterVC se ele já está apresentando um controlador de visualização examinando seu presentedViewController. Se não nil, não diga a ele presentViewController:...- ele já está apresentando um controlador de visualização.

mate
fonte
2
Se o controlador A apresentar o controlador B e, em seguida, B quiser apresentar o UIAlertController, isso funcionaria? Estou tendo o mesmo erro e não consigo descobrir se B já está apresentando algo que eu não conheço, ou se o problema é porque B está sendo apresentado por A
Christopher Francisco
1
@ChristopherFrancisco Faça isso como uma nova pergunta!
matt
@ChristopherFrancisco Olá, estou com o mesmo problema agora, você fez uma nova pergunta para ele? ou onde você consegue resolver isso? se sim como?
Abed Naseri
Ótima resposta, essa é uma distinção sutil.
ScottyBlades
29
if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {

      // UIAlertController is presenting.Here

}
Ben
fonte
22
É sempre uma boa ideia incluir algum texto em sua resposta para explicar o que você está fazendo. Leia como escrever uma boa resposta .
Jørgen R
1
Não é uma boa resposta devido à falta de explicação, mas o método me ajudou muito - o problema é que tive mais de um evento chamando meu código para apresentar um UIAlertControllerdisparo em uma sucessão curta. Verifique se você tem um problema semelhante.
ChidG
10

Bem, as soluções sugeridas acima têm um problema essencial do meu ponto de vista:

Se você perguntar ao seu ViewController, se o atributo 'givenViewController' é nulo e a resposta é falsa, você não pode chegar à conclusão de que seu UIAlertController já está apresentado. Pode ser qualquer ViewController apresentado, por exemplo, um popOver. Portanto, minha sugestão para verificar com certeza se o Alerta já está na tela é a seguinte (lançar o PresentViewController como um UIAlertController):

if self.presentedViewController == nil {
   // do your presentation of the UIAlertController
   // ...
} else {
   // either the Alert is already presented, or any other view controller
   // is active (e.g. a PopOver)
   // ...

   let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?

   if thePresentedVC != nil {
      if let thePresentedVCAsAlertController : UIAlertController = thePresentedVC as? UIAlertController {
         // nothing to do , AlertController already active
         // ...
         print("Alert not necessary, already on the screen !")

      } else {
         // there is another ViewController presented
         // but it is not an UIAlertController, so do 
         // your UIAlertController-Presentation with 
         // this (presented) ViewController
         // ...
         thePresentedVC!.presentViewController(...)

         print("Alert comes up via another presented VC, e.g. a PopOver")
      }
  }

}

LukeSideWalker
fonte
5

Aqui está uma solução que uso no Swift 3. É uma função que mostra um alerta ao usuário e, se você chamá-la várias vezes antes de o usuário dispensar o alerta, ela adicionará o novo texto de alerta ao alerta que já está sendo apresentado . Se alguma outra exibição estiver sendo apresentada, o alerta não aparecerá. Nem todos concordarão com esse comportamento, mas funciona bem para situações simples.

extension UIViewController {
    func showAlert(_ msg: String, title: String = "") {
        if let currentAlert = self.presentedViewController as? UIAlertController {
            currentAlert.message = (currentAlert.message ?? "") + "\n\nUpdate:\(title): \(msg)"
            return
        }

        // create the alert
        let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))

        // show the alert
        self.present(alert, animated: true, completion: nil)
    }
}
biomiker
fonte
OK, isso é o que eu precisava. Também funciona no iOS 13.
Zoltan Vinkler
3

Podemos simplesmente verificar se algum controlador de visualização é apresentado.

se apresentado, verifique se é do tipo UIAlertController.

    id alert = self.presentedViewController;

    if (alert && [alert isKindOfClass:[UIAlertController class]]) 
      {
           *// YES UIAlertController is already presented*
      }
    else
       {
        // UIAlertController is not presented OR visible.
       }
Ravi
fonte
1

você pode testar - em uma única linha - se um alerta já for apresentado:

if self.presentedViewController as? UIAlertController != nil {
    print ("alert already presented")
}
Thierry G.
fonte
Você poderia explicar o código em sua resposta. Ou como adiciona informações relevantes quando já existe uma resposta aceita ou bem avaliada Leia como escrever uma boa resposta
Léa Gris
0

Usei isso para detectar, remover e alertar.

Primeiro, criamos um alerta com a seguinte função.

 var yourAlert :UIAlertController!

 func useYouAlert (header: String, info:String){


    yourAlert = UIAlertController(title:header as String, message: info as String, preferredStyle: UIAlertControllerStyle.alert)



    let okAction = UIAlertAction(title: self.langText[62]as String, style: UIAlertActionStyle.default) { (result : UIAlertAction) -> Void in
        print("OK") 

    }


    yourAlert.addAction(okAction)
    self.present(yourAlert.addAction, animated: true, completion: nil)

}

E em alguma outra parte do seu código

    if yourAlert != nil {

      yourAlert.dismiss(animated: true, completion: nil)

    }
Esperança
fonte
0

Para o idioma Swift mais recente, você pode usar o seguinte:

var alert = presentedViewController

if alert != nil && (alert is UIAlertController) {
    // YES UIAlertController is already presented*
} else {
    // UIAlertController is not presented OR visible.
}
Shahid Aslam
fonte
0

Dispensar o controlador atual e apresentar o controlador de alerta como

 func alert(_ message:String) {
  let alert = UIAlertController(title: "Error!", message: message, preferredStyle: .alert)
  alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
  self.dismiss(animated: false, completion: nil)
  self.present(alert, animated: true,completion: nil)
    }
Faiz Ul Hassan
fonte
0

Resposta Swift 4.2+

if UIApplication.topViewController()!.isKind(of: UIAlertController.self) { 
            print("UIAlertController is presented")}

Para aqueles que não sabem como obter o máximo do Viewcontroller

extension UIApplication {


public class func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
    if let nav = base as? UINavigationController {
        return topViewController(nav.visibleViewController)
    }
    if let tab = base as? UITabBarController {
        if let selected = tab.selectedViewController {
            return topViewController(selected)
        }
    }
    if let presented = base?.presentedViewController {
        return topViewController(presented)
    }
    return base
}}

A resposta do Swift 5+ 'keyWindow' foi descontinuada na edição sugerida do iOS 13.0

if UIApplication.topViewController()!.isKind(of: UIAlertController.self) { 
            print("UIAlertController is presented")}

Para aqueles que não sabem como obter o máximo do Viewcontroller

extension UIApplication {


public class func topViewController(_ base: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
    if let nav = base as? UINavigationController {
        return topViewController(nav.visibleViewController)
    }
    if let tab = base as? UITabBarController {
        if let selected = tab.selectedViewController {
            return topViewController(selected)
        }
    }
    if let presented = base?.presentedViewController {
        return topViewController(presented)
    }
    return base
}}
iOS Lifee
fonte
0

Descobri que precisava criar uma fila para empilhar as solicitações UIAlertController.

NSMutableArray *errorMessagesToShow; // in @interface
errorMessagesToShow=[[NSMutableArray alloc] init];  // in init

-(void)showError:(NSString *)theErrorMessage{
    if(theErrorMessage.length>0){
        [errorMessagesToShow addObject:theErrorMessage];
        [self showError1];
    }
}
-(void)showError1{
    NSString *theErrorMessage;
    if([errorMessagesToShow count]==0)return; // queue finished

    UIViewController* parentController =[[UIApplication sharedApplication]keyWindow].rootViewController;
    while( parentController.presentedViewController &&
      parentController != parentController.presentedViewController ){
        parentController = parentController.presentedViewController;
    }
    if([parentController isKindOfClass:[UIAlertController class]])return;  // busy

    // construct the alert using [errorMessagesToShow objectAtIndex:0]
    //  add to each UIAlertAction completionHandler [self showError1];
    //   then

    [errorMessagesToShow removeObjectAtIndex:0];
    [parentController presentViewController:alert animated:YES completion:nil]; 
}
Peter B. Kramer
fonte
-3

Basta descartar o controlador atual e apresentar o que deseja, ou seja

self.dismiss(animated: false, completion: nil)

self.displayAlertController()

Idelfonso Gutierrez
fonte