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

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.


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

}

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")
      }
  }

}


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

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.
       }

1

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

if self.presentedViewController as? UIAlertController != nil {
    print ("alert already presented")
}

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)

    }

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.
}

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

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
}}

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]; 
}

-3

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

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

self.displayAlertController()

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.