Quelle est la meilleure façon de vérifier si un UIAlertController est déjà présent?

109

J'ai une tableview qui, une fois chargée, chaque cellule pourrait éventuellement renvoyer un NSError, que j'ai choisi d'afficher dans un UIAlertController. Le problème est que j'obtiens cette erreur dans la console si plusieurs erreurs sont renvoyées.

Avertissement: tentative de présenter UIAlertController: 0x14e64cb00 sur MessagesMasterVC: 0x14e53d800 qui présente déjà (null)

Idéalement, je voudrais idéalement gérer cela dans ma méthode d'extension 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
}

Sur la base de la réponse de matt, j'ai changé l'extension en une extension UIViewController, elle est beaucoup plus propre et économise beaucoup de code 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)
    }
}
hidden-username
la source
Merci d'avoir publié votre code mis à jour.
djbp
J'ai également déplacé le reste du code (trois lignes pour configurer le UIAlertController) dans l'instruction If, car cela donnait toujours l'erreur suivante (Tenter de charger la vue d'un contrôleur de vue pendant sa désallocation n'est pas autorisé et peut entraîner comportement indéfini)
Kitson
Je voudrais consulter la solution sur le lien ci-dessous, veuillez consulter stackoverflow.com/a/39994115/1872233
iDevAmit

Réponses:

119

Ce n'est pas l'UIAlertController qui "présente déjà", c'est MessagesMasterVC. Un contrôleur de vue ne peut présenter qu'un seul autre contrôleur de vue à la fois. D'où le message d'erreur.

En d'autres termes, si vous avez demandé à un contrôleur de vue presentViewController:..., vous ne pouvez pas le faire à nouveau tant que le contrôleur de vue présenté n'a pas été supprimé.

Vous pouvez demander au MessagesMasterVC s'il présente déjà un contrôleur de vue en examinant son presentedViewController. Sinon nil, ne le lui dites pas presentViewController:...- il présente déjà un contrôleur de vue.

mat
la source
2
Si le contrôleur A présente le contrôleur B, puis B veut présenter UIAlertController, cela fonctionnerait-il? J'ai la même erreur et je ne peux pas comprendre si B présente déjà quelque chose que je ne sais pas, ou si le problème est parce que B est présenté par A
Christopher Francisco
1
@ChristopherFrancisco Posez cela comme une nouvelle question!
mat
@ChristopherFrancisco Salut, j'ai le même problème maintenant, as-tu posé une nouvelle question? ou où vous êtes en mesure de le résoudre? si oui, comment?
Abed Naseri le
Excellente réponse, c'est une distinction subtile.
ScottyBlades
29
if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {

      // UIAlertController is presenting.Here

}
Ben
la source
22
C'est toujours une bonne idée de mettre du texte dans votre réponse pour expliquer ce que vous faites. Lisez comment rédiger une bonne réponse .
Jørgen R
1
Pas une bonne réponse en raison d'un manque d'explications, mais la méthode m'a beaucoup aidé - le problème était que j'avais plus d'un événement appelant mon code pour présenter un UIAlertControllertir en courte succession. Vérifiez ceci si vous rencontrez un problème similaire.
ChidG
10

Eh bien, les solutions suggérées ci-dessus présentent un problème essentiel de mon point de vue:

Si vous demandez à votre ViewController, si l'attribut 'presentViewController' est nul et la réponse est fausse, vous ne pouvez pas arriver à la conclusion que votre UIAlertController est déjà présenté. Il peut s'agir de n'importe quel ViewController présenté, par exemple un popOver. Donc, ma suggestion pour vérifier sûrement si l'alerte est déjà à l'écran est la suivante (transtypez le présentéViewController en 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
la source
5

Voici une solution que j'utilise dans Swift 3. C'est une fonction qui affiche une alerte à l'utilisateur, et si vous l'appelez plusieurs fois avant que l'utilisateur n'ait ignoré l'alerte, elle ajoutera le nouveau texte d'alerte à l'alerte qui est déjà présentée . Si une autre vue est présentée, l'alerte n'apparaîtra pas. Tous ne seront pas d'accord avec ce comportement, mais cela fonctionne bien pour des situations 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
la source
OK, c'est ce dont j'avais besoin. Cela fonctionne également dans iOS 13.
Zoltan Vinkler
3

Nous pouvons simplement vérifier si un contrôleur de vue est présenté.

s'il est présenté, vérifiez s'il s'agit d'une sorte de UIAlertController.

    id alert = self.presentedViewController;

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

vous pouvez tester - sur une seule ligne - si une alerte est déjà présentée:

if self.presentedViewController as? UIAlertController != nil {
    print ("alert already presented")
}
Thierry G.
la source
Vous pourriez expliquer le code dans votre réponse. Ou comment il ajoute des informations pertinentes quand il y a déjà une réponse acceptée ou bien notée Lire comment écrire une bonne réponse
Léa Gris
0

Je l'ai utilisé pour détecter, supprimer et alerter.

Nous créons d'abord une alerte avec la fonction suivante.

 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)

}

Et dans une autre partie de votre code

    if yourAlert != nil {

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

    }
Espérer
la source
0

Pour la dernière langue Swift, vous pouvez utiliser ce qui suit:

var alert = presentedViewController

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

Ignorer le contrôleur actuel et présenter le contrôleur d'alerte comme

 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
la source
0

Réponse Swift 4.2+

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

Pour ceux qui ne savent pas comment obtenir le plus de 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
}}

La réponse Swift 5+ 'keyWindow' est obsolète dans iOS 13.0 Modification suggérée

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

Pour ceux qui ne savent pas comment obtenir le plus de 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
}}
Vie iOS
la source
0

J'ai trouvé que j'avais besoin de créer une file d'attente pour empiler les demandes 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
la source
-3

Ignorez simplement le contrôleur actuel et présentez celui que vous voulez ie

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

self.displayAlertController()

Idelfonso Gutierrez
la source