Qual è il modo migliore per verificare se un UIAlertController sta già presentando?


109

Ho un tableview che, una volta caricato, ogni cella potrebbe eventualmente restituire un NSError, che ho scelto di visualizzare in un UIAlertController. Il problema è che ottengo questo errore nella console se vengono restituiti più errori.

Avviso: tentativo di presentare UIAlertController: 0x14e64cb00 su MessagesMasterVC: 0x14e53d800 che sta già presentando (null)

Idealmente, mi piacerebbe idealmente gestirlo nel mio metodo di estensione 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
}

Sulla base della risposta di matt, ho cambiato l'estensione in un'estensione UIViewController, è molto più pulita e consente di risparmiare molto codice 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)
    }
}

Grazie per aver pubblicato il tuo codice aggiornato.
djbp

Ho anche spostato il resto del codice (tre righe per configurare UIAlertController) nell'istruzione If, ​​perché continuava a fornire il seguente errore (il tentativo di caricare la vista di un controller di visualizzazione mentre si sta deallocando non è consentito e potrebbe causare comportamento indefinito)
Kitson

Vorrei fare riferimento alla soluzione sui link qui sotto, si prega di controllare stackoverflow.com/a/39994115/1872233
iDevAmit

Risposte:


119

Non è l'UIAlertController che "sta già presentando", è MessagesMasterVC. Un controller di visualizzazione può presentare solo un altro controller di visualizzazione alla volta. Da qui il messaggio di errore.

In altre parole, se hai detto a un controller di visualizzazione di presentViewController:..., non puoi farlo di nuovo finché il controller di visualizzazione presentato non è stato ignorato.

Puoi chiedere a MessagesMasterVC se sta già presentando un controller di visualizzazione esaminando il suo presentedViewController. In caso contrario nil, non dirglielo presentViewController:...: sta già presentando un controller di visualizzazione.


2
Se il controller A presenta il controller B, e quindi B vuole presentare UIAlertController, funzionerebbe? Sto riscontrando lo stesso errore e non riesco a capire se B sta già presentando qualcosa che non conosco, o se il problema è perché B è stato presentato da A
Christopher Francisco

1
@ChristopherFrancisco Chiedilo come nuova domanda!
matt

@ChristopherFrancisco Ciao, ho lo stesso problema ora, hai fatto una nuova domanda? o dove sei riuscito a risolverlo? se si, come?
Abed Naseri

Ottima risposta, questa è una sottile distinzione.
ScottyBlades

29
if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {

      // UIAlertController is presenting.Here

}

22
È sempre una buona idea inserire del testo nella risposta per spiegare cosa stai facendo. Leggi come scrivere una buona risposta .
Jørgen R

1
Non un'ottima risposta a causa della mancanza di spiegazioni, ma il metodo mi ha aiutato molto: il problema era che avevo più di un evento che chiamava il mio codice per presentare uno UIAlertControllersparo in breve successione. Controlla questo se hai un problema simile.
ChidG

10

Bene, le soluzioni suggerite sopra hanno un problema essenziale dal mio punto di vista:

Se chiedi al tuo ViewController se l'attributo "presentatoViewController" è nullo e la risposta è falsa, non puoi giungere alla conclusione che il tuo UIAlertController è già presentato. Potrebbe essere qualsiasi ViewController presentato, ad esempio un popOver. Quindi il mio suggerimento per verificare sicuramente se l'avviso è già sullo schermo è il seguente (lanciare il presentViewController come 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

Ecco una soluzione che utilizzo in Swift 3. È una funzione che mostra un avviso all'utente e se lo chiami più volte prima che l'utente abbia ignorato l'avviso, aggiungerà il nuovo testo dell'avviso all'avviso che è già stato presentato . Se viene presentata un'altra visualizzazione, l'avviso non verrà visualizzato. Non tutti saranno d'accordo con quel comportamento, ma funziona bene per situazioni semplici.

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, questo è ciò di cui avevo bisogno. Funziona anche su iOS 13.
Zoltan Vinkler

3

Possiamo semplicemente controllare se viene presentato un controller di visualizzazione.

se presentato, controlla se è una specie di UIAlertController.

    id alert = self.presentedViewController;

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

1

puoi testare - in una sola riga - se un avviso è già presentato:

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

Potresti spiegare il codice nella tua risposta. O come aggiunge informazioni rilevanti quando c'è già una risposta accettata o molto apprezzata Leggi come scrivere una buona risposta
Léa Gris


0

L'ho usato per rilevare, rimuovere e avvisare.

Per prima cosa creiamo un avviso con la seguente funzione.

 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 in qualche altra parte del codice

    if yourAlert != nil {

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

    }

0

Per l'ultima lingua Swift puoi usare quanto segue:

var alert = presentedViewController

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

0

Chiudi il controller corrente e presenta il controller degli avvisi come

 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

Risposta rapida 4.2+

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

Per coloro che non sanno come ottenere il massimo dal 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
}}

Swift 5+ Answer 'keyWindow' è stato deprecato nella modifica suggerita da iOS 13.0

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

Per coloro che non sanno come ottenere il massimo dal 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

Ho scoperto che avevo bisogno di creare una coda per impilare le richieste di 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

Chiudi semplicemente il controller corrente e presenta quello che desideri, ad es

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

self.displayAlertController()

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.