[Résolu] Erreur : Attempt to present UIAlertController which is already presenting !!!

busterTheobusterTheo Membre
février 2016 modifié dans API UIKit #1

Bonjour tous,


après avoir parcouru pour une seconde fois plein de pages sur le web sur le sujet, je reviens au bar, car je n'ai pas trouvé la solution. J'ai fait tous les tests possibles, et je ne comprend vraiment pas ce truc.


 


Dans un autre de mes viewControllers, j'ai eu le même problème, que j'ai résolu en mettant mon code d'alerte dans le viewDidAppear. Mais c'était logique, car j'avais besoin de cette alerte à  l'entrée sur la vue correspondante.



override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)

let ecartLignesPixels = abs((poigneeTopRightX + poigneeBottomRightX)/2 - poigneeTopCenterX)
let ecartLignesMms = round((CGFloat(ecartLignesPixels) * mesureMm / mesure)*1000) / 1000

if ecartLignesPixels == ecartLignesMms {
let alertMesures: UIAlertController = UIAlertController(title: "Mesures", message: "Attention,\ntous les calculs seront faux.\nRetournez sur l'écran d'accueil,\ncliquez sur la photo 3, puis,\nentrez les mesures en pixel et mm.", preferredStyle: .Alert)
let fermerMesures = UIAlertAction(title: "Fermer", style: .Cancel) { action -> Void in
pagesNavigationController.popToRootViewControllerAnimated(true)
}
alertMesures.addAction(fermerMesures)
self.presentViewController(alertMesures, animated: true, completion: nil)
}
}

Par contre, là  j'ai besoin d'une alerte, après avoir cliqué dans une dans une view déjà  présentée en popoverController. Donc au sein de la méthode suivante



func bouches2eNormalTap(recognizer: UITapGestureRecognizer) {
let tap = recognizer as UITapGestureRecognizer

if tap.state == .Ended {
.....

Je ne met que le code utile.



// Alert du choix si on ajoute
let alertImagePicto2: UIAlertController = UIAlertController(title: "Second choix", message: "àŠtes-vous certain de vouloir ajouter un second choix ?", preferredStyle: .Alert)

// Ajout - Donc insertion imagepicto2
let ajoutImagePicto2 = UIAlertAction(title: "Ajouter", style: .Destructive) { action -> Void in
self.imagePicto.frame = CGRectMake(14,self.boutonPositionY - 95, 113, 75)
self.imagePicto2 = UIImageView(frame: CGRectMake(143,self.boutonPositionY - 95, 113, 75))
self.imagePicto2.backgroundColor = FondsColor
self.imagePicto2.layer.shadowColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.40).CGColor
self.imagePicto2.layer.shadowOffset = CGSize(width: 2, height: 2)
self.imagePicto2.layer.shadowOpacity = 0.5
self.imagePicto2.layer.shadowRadius = 10
self.imagePicto2.layer.cornerRadius = 5

self.e2L5Position2String = self.labelsResultat[0]

self.bouches2eViewController.label2eNormal.textColor = self.ChoixColor
}
alertImagePicto2.addAction(ajoutImagePicto2)

// Pas d'ajout - Donc modification imagepicto (1)
let noAjoutImagePicto2 = UIAlertAction(title: "Annuler", style: .Cancel) { action -> Void in
self.e2L5PositionString = self.labelsResultat[0]

self.bouches2eViewController.label2eNormal.textColor = self.ChoixColor
self.bouches2eViewController.label2eProalveolie.textColor = self.BaseColor
self.bouches2eViewController.label2eRetroalveolie.textColor = self.BaseColor
self.bouches2eViewController.label2eRetrusion.textColor = self.BaseColor
self.bouches2eViewController.label2eProtrusion.textColor = self.BaseColor

self.dismissViewControllerAnimated(true, completion: nil)

self.imagePicto.image = UIImage(named: self.imagesResultat[0])
}
alertImagePicto2.addAction(noAjoutImagePicto2)

self.presentViewController(alertImagePicto2, animated: true, completion: nil)

Et voici  l'erreur :



 


Warning: Attempt to present <UIAlertController: 0x7f89d3153030>  on <GuideEsthetique.Etape2L5ViewController: 0x7f89d188b800> which is already presenting (null)



 


Si quelqu'un a une idée lumineuse, parce que là , je sèche.


 


Merci d'avance.


«1

Réponses

  • CéroceCéroce Membre, Modérateur
    Mets un point d'arrêt symbolique (symbolic breakpoint) sur -[UIViewController presentViewController:animated:completion:].
    ça devrait te permettre de voir dans quelles circonstances exactes il est appelé.
  • busterTheobusterTheo Membre
    février 2016 modifié #3

    Merci Ceroce, mais je sais exactement quand...


  • CéroceCéroce Membre, Modérateur
    Non, si tu maà®trisais le cycle d'apparition de ton alerte, tu n'aurais pas ce warning. Le warning semble t'indiquer que la méthode est appelée deux fois de suite. Il faut que tu en saches plus.
  • Bon, j'en ai mis un pour voir.


    Et ça ne m'apprend rien, si ce n'est que ça me met la puce à  l'oreille sur ce que je savais déjà , et expliqué plus haut.


     


    Soit, je suis déjà  sur un gesture recognizer... dans un popover...


  • busterTheobusterTheo Membre
    février 2016 modifié #6

    Deux fois de suite. Pour moi c'est impossible.


    C'est dans un UITapGestureRecognizer



    if tap.state == .Ended {
    blablabla
    self.presentViewController(alertImagePicto2, animated: true, completion: nil)
    }

  • CéroceCéroce Membre, Modérateur
    février 2016 modifié #7
    Mais est-ce que ce sont les mêmes instances ?
    Parce que ce qui arrive souvent c'est qu'on ne comprenne pas qu'une méthode soit appelée deux fois de suite jusqu'à  qu'on se rende compte qu'elle est appelée sur deux instances différentes, par exemple.
  • Heu, ben non j'ai un gestore et un alert - tout est différent et pas nommé pareil


  • Ah, ben j'ai plusieurs recognizer dans mon popover (j'ai des pictos sur lesquels je clique)


    Mais pour l'instant je n'ai mis qu'une alerte sur le premier picto pour tester clean et facile.


    Donc je n'ai qu'une instance...


  • CéroceCéroce Membre, Modérateur
    février 2016 modifié #10
    Regarde les ADRESSES des objets.
    Regarde si c'est bien l'instance de UIAlertController que tu attends (il a le bon message ?).
  • busterTheobusterTheo Membre
    février 2016 modifié #11

    Je sais pas faire :'(


  • en survol je crois les voir mais tout semble unique


  • CéroceCéroce Membre, Modérateur
    OK. Procédons autrement.
    Essaie de désactiver ton gesture recognizer.
  • busterTheobusterTheo Membre
    février 2016 modifié #14

    Ecoute, je pense avoir une piste, mais ne sait pas comment la suivre.


     


    Il se trouve que comme je suis dans un popover, et que je puisse cliqué sur chacun des 5 pictos inclus dedans (chacun son gesture), il est évident que j'ai un :



    self.dismissViewControllerAnimated(true, completion: nil)

    pour fermer le popover après le click.


     


    D'ailleurs tous les autres clicks fonctionnent.


     


    Mais sur le premier picto (et ce sera sur tous), j'ai mis une alerte, et donc j'ai aussi un :



    self.dismissViewControllerAnimated(true, completion: nil)

    mais ce coup-ci, c'est pour fermer l'alert.


     


    Donc, c'est sur, y'a conflit...


     


    C'est chaud, hein...


  • Si la cause est bien le conflit que tu suspectes, pourquoi ne pas afficher ton alerte après la fermeture du popover via une notification avec un délai ?


  • CéroceCéroce Membre, Modérateur
    Oui, je comprends, au tap, il y a deux possibilités:
    - soit on ferme le popover et on applique l'action
    - soit on affiche une alerte.

    En fait, ce n'est pas si compliqué: là , tu fermes le popover dès qu'on tap... il ne faut pas. Il faut juste que tu évalues la condition d'affichage de l'alerte avant.
  • CéroceCéroce Membre, Modérateur

    Si la cause est bien le conflit que tu suspectes, pourquoi ne pas afficher ton alerte après la fermeture du popover via une notification avec un délai ?

    Sans doute parce qu'il veut conserver le popover ouvert.
  • Eric P.Eric P. Membre
    février 2016 modifié #18


    Sans doute parce qu'il veut conserver le popover ouvert.




    Possible mais je préfère poser une question évidente plutôt qu'interpréter les dires des autres.


    L'expérience montre que les réponses sont souvent différentes.


  • Ben tout ce que venez de dire est valable.


     


    J'ai donc mis tout le code dans un



    if tap.state == .Began {

    Et le code pour fermer le popover dans



    if tap.state == .Ended {
    dismissViewControllerAnimated(true, completion: nil)
    }

    Du coup, plus d'erreur, mais plus d'alerte - grrrr

  • UITapGestureRecognizer


     


    seulement le ended pas de began ni changed


     


    pas de chance  :'(


  • Je crois que je vais essayer le délai dont parle Eric P.


     


    En espérant que ça fonctionne


  • busterTheobusterTheo Membre
    février 2016 modifié #22

    Bon, j'ai essayé avec un timer - C'est pareil.


    J'ai essayé en supprimant tous les dismiss. C'est pareil.


    J'ai essayé en ne gardant que mon action .Cancel. C'est pareil.


     


    Je met donc un code simplifié de base pour les docteur barmen


     


    ça, ça marche nickel :



    override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    let uneAlerte: UIAlertController = UIAlertController(title: "Un titre", message: "Un message", preferredStyle: .Alert)

    let uneAction = UIAlertAction(title: "Annuler", style: .Cancel) { action -> Void in
    print("Yeah ca marche")
    }
    uneAlerte.addAction(uneAction)

    self.presentViewController(uneAlerte, animated: true, completion: nil)
    }

    ça, ça marche pas (même code) :



    func bouches2eNormalTap(recognizer: UITapGestureRecognizer) {
    if recognizer.state == .Ended {
    let uneAlerte: UIAlertController = UIAlertController(title: "Un titre", message: "Un message", preferredStyle: .Alert)

    let uneAction = UIAlertAction(title: "Annuler", style: .Cancel) { action -> Void in
    print("Yeah ca marche")
    }
    uneAlerte.addAction(uneAction)

    self.presentViewController(uneAlerte, animated: true, completion: nil)
    }
    }

    C'est un problème de fond qui m'échappe.


     


    Y'a plus de dismiss, plus de conflit, etc... Et toujours cette erreur.



     


    2016-02-22 17:53:33.964 GuideEsthetique[74699:4263852] Warning: Attempt to present <UIAlertController: 0x7f897601d870>  on <GuideEsthetique.Etape2L5ViewController: 0x7f8975013800> which is already presenting (null)



     


    Génial... Qu'est-ce-qu'on s'éclate lorsque l'on manque de connaissance. C'est ça l'aventure. :p


  • colas_colas_ Membre
    février 2016 modifié #23

    Si tu ajoutes un dans 



    if recognizer.state == .Ended {[...]}

    un println("Test"), il ne l'affiche bien qu'une fois ?


     


     


    PS : bien maà®triser les points d'arrêt, etc. (si tu n'es pas ok là -dessus), c'est un investissement qui te fera gagner 1000 heures.


  • oui oui oui - merci quand même


  • C'est quand même dingue que sur le web (je n'arrête pas de changer mes requêtes) on ne trouve strictement rien sur les problèmes concernant l'apparition d'une alerte suite à  un click dans un popOver.


     


    Je reconnais que c'est chaud de faire apparaà®tre une fenêtre popUp à  partir d'une autre fenêtre popUp.


    Mais je développe pour le web (sites web - php,  jQuery, etc...), et là  on peut imbriquer n'importe quoi dans n'importe quoi, en respectant les règles, évidemment. Il suffit de bien nommer ses variables. Là , chez Apple, y'a quand même un problème de concept de chez concept, non ?


     


    Ou bien, mon histoire est débile, à  savoir prévenir l'utilisateur, de quelque chose (...), suite à  un click dans un popOver.


     


    Je crois que je vais me faire une view perso, mais c'est super relou quand même. Booouuuuhhh...


  • Purée, à  force de faire plein de trucs débiles, je crois avoir trouvé.


     


    C'est vraiment débile.


    J'avais toutes les infos dans les réponses à  mon post, mais il fallait juste remettre de l'ordre dans tout ça, et aussi dans mon cerveau.


     


    Il fallait juste fermer le popOver avant la détection du state .Ended. C'est vraiment con tout ça.


     


    Donc, pour ceux qui auraient besoin de ce genre de trucs qui semble très rarement utilisé, voici le code :



    func bouches2eNormalTap(recognizer: UITapGestureRecognizer) {

    self.dismissViewControllerAnimated(true, completion: nil)

    if recognizer.state == .Ended {
    let uneAlerte: UIAlertController = UIAlertController(title: "Un titre", message: "Un message", preferredStyle: .Alert)

    let uneAction = UIAlertAction(title: "Annuler", style: .Cancel) { action -> Void in
    print("Yeah ca marche")
    }
    uneAlerte.addAction(uneAction)

    self.presentViewController(uneAlerte, animated: true, completion: nil)
    }
    }

    Encore merci pour l'aide des barmen, sans laquelle je n'aurai jamais trouvé.


      :p   :D   :p


  • Joanna CarterJoanna Carter Membre, Modérateur



    override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    let uneAlerte: UIAlertController = UIAlertController(title: "Un titre", message: "Un message", preferredStyle: .Alert)

    let uneAction = UIAlertAction(title: "Annuler", style: .Cancel) { action -> Void in
    print("Yeah ca marche")
    }
    uneAlerte.addAction(uneAction)

    self.presentViewController(uneAlerte, animated: true, completion: nil)
    }

    ça, ça marche pas (même code) :



    func bouches2eNormalTap(recognizer: UITapGestureRecognizer) {
    if recognizer.state == .Ended {
    let uneAlerte: UIAlertController = UIAlertController(title: "Un titre", message: "Un message", preferredStyle: .Alert)

    let uneAction = UIAlertAction(title: "Annuler", style: .Cancel) { action -> Void in
    print("Yeah ca marche")
    }
    uneAlerte.addAction(uneAction)

    self.presentViewController(uneAlerte, animated: true, completion: nil)
    }
    }



     


    Je viens de créer un nouveau projet et de copier/coller ton code ci-dessus, en connectant la fonction bouches2eNormalTap à  un UITapRecognizer sur la View principale, et tous marche bien.


     


    Bien sûr il y a quelques autres problèmes avec ton code, niveau transmission de self dans un closure, niveau fermeture d'une vue juste après son ouverture, mais je les laisse pour le moment.

  • Ah, salut Joanna Carter.


    Je savais que t'allais venir...    Cool...


     


    Excuse moi, si je te répond tardivement.


     


    Bon, tu as du voir, que j'ai trouvé la solution...



    self.dismissViewControllerAnimated(true, completion: nil)

    avant toute détection.


     


    C'est Eric P. qui as mis le doigt dessus, en fait. Mais j'ai mis du temps à  comprendre. Et Ceroce, aussi m'a bien aidé.


     


    Sans quoi, pour te répondre plus précisément :


     


    Je sais bien que les self sont indispensables dans les closures. De toute façon, c'est Xcode qui me le dit, et non pas ma culture, quoique aujourd'hui, je le sais, grave (à  force... d;-)).


     


    En tout cas, ta remarque me donne envie de faire comme toi. De créer un projet vide et nouveau et d'intégrer ce code (le code simple ci-dessus, on est bien d'accord), et de voir le même résultat que toi. Bien que je sois sceptique, mais j'ai plutôt confiance en toi qu'en mes supputations personnelles...  ::)


     


    Et je suis tellement la tête dans le guidon (commDab'), que je ferais cela bien plus tard si j'y repense. Je suis désolé pour toi, enfin surtout pour moi, et je sais que tu t'en fous un peu, et que t'as bien raison... ::)


     


    En gros, merci au bar. Je met Résolu, mais je reste quand même attentif à  toutes remarques...Qui m'intéressent énormément.


     


    :p   :p   :p   :D   :p   :p   :p

  • busterTheobusterTheo Membre
    février 2016 modifié #29

    Au fait, j'oubliais, je met le code complet pour ceux que ça intéresse.


    Même si cela n'est pas super passionnant.


    Enfin, y'a les commentaires qui sont presque, à  mon goût, plus intéressants que le code.


     


    Je précise que bouches2eViewController est d'après un XIB avec son vieswcontroller qui contient les outlets (genre label2eNormal...).


     


    Et je me rend compte que les self.dismissViewControllerAnimated(true, completion: nil) ne servent à  rien, en fait. ça, ça m'épate grave.


     


    1- le code sans alerte, qui fonctionne parfaitement :



    func bouches2eNormalTap(recognizer: UITapGestureRecognizer) {
    if recognizer.state == .Ended {
    e2L5PositionString = labelsResultat[0]

    bouches2eViewController.label2eNormal.textColor = ChoixColor
    bouches2eViewController.label2eProalveolie.textColor = BaseColor
    bouches2eViewController.label2eRetroalveolie.textColor = BaseColor
    bouches2eViewController.label2eRetrusion.textColor = BaseColor
    bouches2eViewController.label2eProtrusion.textColor = BaseColor

    self.dismissViewControllerAnimated(true, completion: nil)

    imagePicto.image = UIImage(named: imagesResultat[0])

    resultatRoue.removeFromSuperview()
    roueResultat()

    enregistrer()
    }
    }

    2- Le code avec alerte, qui fonctionne maintenant, heu, parfaitement (enfin, il semble, pour l'instant)  :



    func bouches2eNormalTap(recognizer: UITapGestureRecognizer) {

    self.dismissViewControllerAnimated(true, completion: nil)

    if recognizer.state == .Ended {

    // On teste si le picto sur lequel on clique n'est pas actif - (Sinon, on ne fera rien)
    if bouches2eViewController.label2eNormal.textColor != ChoixColor {

    // On teste si imagepicto existe et imagepicto2 n'existe pas - Donc on demande si on rajoute un picto (2) ou si on modifie le picto 1
    if e2L5PositionString != "" && e2L5Position2String == "" {
    // Alert du choix si on ajoute ou on modifie
    let alertImagePicto2: UIAlertController = UIAlertController(title: "Second choix", message: "Voulez-vous ajouter un second choix\nou modifier le premier choix ?", preferredStyle: .Alert)

    // Ajout - Donc insertion imagepicto2
    let ajoutImagePicto2 = UIAlertAction(title: "Ajouter", style: .Destructive) { action -> Void in
    self.imagePicto.frame = CGRectMake(14,self.boutonPositionY - 95, 113, 75)
    self.imagePicto2 = UIImageView(frame: CGRectMake(143,self.boutonPositionY - 95, 113, 75))
    self.imagePicto2.backgroundColor = FondsColor
    self.imagePicto2.layer.shadowColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.40).CGColor
    self.imagePicto2.layer.shadowOffset = CGSize(width: 2, height: 2)
    self.imagePicto2.layer.shadowOpacity = 0.5
    self.imagePicto2.layer.shadowRadius = 10
    self.imagePicto2.layer.cornerRadius = 5

    self.e2L5Position2String = self.labelsResultat[0]

    self.bouches2eViewController.label2eNormal.textColor = self.ChoixColor

    self.imagePicto2.image = UIImage(named: self.imagesResultat[0])

    self.resultatRoue.removeFromSuperview()
    self.roueResultat()

    self.enregistrer()
    }
    alertImagePicto2.addAction(ajoutImagePicto2)

    // Pas d'ajout - Donc modification imagepicto (1)
    let noAjoutImagePicto2 = UIAlertAction(title: "Modifier", style: .Cancel) { action -> Void in
    self.e2L5PositionString = self.labelsResultat[0]

    self.bouches2eViewController.label2eNormal.textColor = self.ChoixColor
    self.bouches2eViewController.label2eProalveolie.textColor = self.BaseColor
    self.bouches2eViewController.label2eRetroalveolie.textColor = self.BaseColor
    self.bouches2eViewController.label2eRetrusion.textColor = self.BaseColor
    self.bouches2eViewController.label2eProtrusion.textColor = self.BaseColor

    self.imagePicto.image = UIImage(named: self.imagesResultat[0])

    self.resultatRoue.removeFromSuperview()
    self.roueResultat()

    self.enregistrer()
    }
    alertImagePicto2.addAction(noAjoutImagePicto2)

    self.presentViewController(alertImagePicto2, animated: true, completion: nil)
    }

    // On teste si imagepicto existe et imagepicto2 existe - Donc on demande lequel on modifie
    if e2L5PositionString != "" && e2L5Position2String != "" {
    // Alert du choix laquelle on modifie
    let alertModifQuelleImage: UIAlertController = UIAlertController(title: "Second choix", message: "Quel choix voulez-vous modifier ?", preferredStyle: .Alert)

    // Modification imagepicto (1)
    let modifImagePicto1 = UIAlertAction(title: "Premier choix", style: .Destructive) { action -> Void in
    self.e2L5PositionString = self.labelsResultat[0]

    self.bouches2eViewController.label2eNormal.textColor = self.ChoixColor

    self.imagePicto.image = UIImage(named: self.imagesResultat[0])

    self.resultatRoue.removeFromSuperview()
    self.roueResultat()

    self.enregistrer()
    }
    alertModifQuelleImage.addAction(modifImagePicto1)

    // Modification imagepicto2
    let modifImagePicto2 = UIAlertAction(title: "Second choix", style: .Destructive) { action -> Void in
    self.e2L5Position2String = self.labelsResultat[0]

    self.bouches2eViewController.label2eNormal.textColor = self.ChoixColor

    self.imagePicto2.image = UIImage(named: self.imagesResultat[0])

    self.resultatRoue.removeFromSuperview()
    self.roueResultat()

    self.enregistrer()
    }
    alertModifQuelleImage.addAction(modifImagePicto2)

    self.presentViewController(alertModifQuelleImage, animated: true, completion: nil)
    }

    // On teste si aucun n'existe - donc on installe le premier
    if e2L5PositionString == "" && e2L5Position2String == "" {
    e2L5PositionString = labelsResultat[0]

    bouches2eViewController.label2eNormal.textColor = ChoixColor
    bouches2eViewController.label2eProalveolie.textColor = BaseColor
    bouches2eViewController.label2eRetroalveolie.textColor = BaseColor
    bouches2eViewController.label2eRetrusion.textColor = BaseColor
    bouches2eViewController.label2eProtrusion.textColor = BaseColor

    imagePicto.image = UIImage(named: imagesResultat[0])

    self.resultatRoue.removeFromSuperview()
    self.roueResultat()

    self.enregistrer()
    }
    }
    }
    }

    À la votre   :p


  • CéroceCéroce Membre, Modérateur

    J'avais toutes les infos dans les réponses à  mon post, mais il fallait juste remettre de l'ordre dans tout ça, et aussi dans mon cerveau.
    Il fallait juste fermer le popOver avant la détection du state .Ended. C'est vraiment con tout ça.

    Oui, c'est ce que je te disais de faire hier soir.

    Quand on a passé une journée entière sur un problème, on a du mal à  en voir l'issue. C'est l'une des limites du travail solitaire: on n'a pas un collègue pour jeter un oe“il neuf (et moins fatigué).

    Je n'avais pas la solution en lisant ton code, mais je t'ai incité à  changer de méthode de débogage, et c'est ça qui a décoincé la situation. Pas la méthode de débogage, mais le fait que tu prennes un peu de recul. La solution ne se trouvait pas ton code, mais dans ta tête.
  • FKDEVFKDEV Membre
    février 2016 modifié #31
    Je crois qu'il faut éviter d'enchainer les appels de dismiss et present de la manière suivante:
     
    self.dismissViewControllerAnimated(true, completion: nil)
    self.presentViewController(alertImagePicto2, animated: true, completion: nil)
    mais plutôt le faire de la manière suivante:
     
    self.dismissViewControllerAnimated(true, completion: {
    self.presentViewController(alertImagePicto2, animated: true, completion: nil)
    })
    En tous cas, il me semble que dans le passé j'ai eu des problèmes avec ce genre d'enchaà®nements.
Connectez-vous ou Inscrivez-vous pour répondre.