Wie kann am besten überprüft werden, ob ein UIAlertController bereits vorhanden ist?

109

Ich habe eine Tabellenansicht, in der beim Laden möglicherweise jede Zelle einen NSError zurückgibt, den ich in einem UIAlertController angezeigt habe. Problem ist, dass ich diesen Fehler in der Konsole erhalte, wenn mehrere Fehler zurückgegeben werden.

Warnung: Versuch, UIAlertController: 0x14e64cb00 in MessagesMasterVC: 0x14e53d800 zu präsentieren, das bereits präsentiert wird (null)

Im Idealfall möchte ich dies in meiner UIAlertController-Erweiterungsmethode behandeln.

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
}

Basierend auf der Antwort von matt habe ich die Erweiterung in eine UIViewController-Erweiterung geändert, die viel sauberer ist und viel presentViewController-Code speichert.

    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)
    }
}
versteckter Benutzername
quelle
Vielen Dank, dass Sie Ihren aktualisierten Code veröffentlicht haben.
DJBP
Ich habe auch den Rest des Codes (drei Zeilen zum Einrichten des UIAlertControllers) in die If-Anweisung verschoben, da immer noch der folgende Fehler ausgegeben wurde (Der Versuch, die Ansicht eines Ansichtscontrollers während der Freigabe zu laden, ist nicht zulässig und kann dazu führen undefiniertes Verhalten)
Kitson
Ich möchte die Lösung auf den folgenden Link verweisen, überprüfen Sie
iDevAmit

Antworten:

119

Es ist nicht der UIAlertController, der "bereits präsentiert", sondern MessagesMasterVC. Ein Ansichtscontroller kann jeweils nur einen anderen Ansichtscontroller präsentieren. Daher die Fehlermeldung.

Mit anderen Worten, wenn Sie einem Ansichts-Controller angewiesen haben presentViewController:..., dies nicht erneut zu tun, bis der dargestellte Ansichts-Controller entlassen wurde.

Sie können den MessagesMasterVC fragen, ob er bereits einen Ansichts-Controller präsentiert, indem Sie dessen untersuchen presentedViewController. Wenn nicht nil, sagen Sie es nicht presentViewController:...- es wird bereits ein View Controller angezeigt.

matt
quelle
2
Wenn Controller A Controller B präsentiert und B dann UIAlertController präsentieren möchte, würde das funktionieren? Ich habe den gleichen Fehler und kann nicht herausfinden, ob B bereits etwas präsentiert, von dem ich nichts weiß, oder ob das Problem darin besteht, dass B von A präsentiert wird
Christopher Francisco
1
@ChristopherFrancisco Stellen Sie das als neue Frage!
Matt
@ChristopherFrancisco Hallo, ich habe jetzt das gleiche Problem, hast du eine neue Frage dazu gestellt? oder wo kannst du es lösen? wenn ja wie?
Abed Naseri
Tolle Antwort, das ist eine subtile Unterscheidung.
ScottyBlades
29
if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {

      // UIAlertController is presenting.Here

}
Ben
quelle
22
Es ist immer eine gute Idee, einen Text in Ihre Antwort einzufügen, um zu erklären, was Sie tun. Lesen Sie, wie Sie eine gute Antwort schreiben .
Jørgen R
1
Keine gute Antwort, da es an Erklärungen mangelt, aber die Methode hat mir sehr geholfen - das Problem war, dass ich mehr als ein Ereignis hatte, das meinen Code aufrief, um einen UIAlertControllerBrand in kurzer Folge zu präsentieren. Überprüfen Sie dies, wenn Sie ein ähnliches Problem haben.
ChidG
10

Nun, die oben vorgeschlagenen Lösungen haben aus meiner Sicht ein wesentliches Problem:

Wenn Sie Ihren ViewController fragen, ob das Attribut 'presentViewController' gleich Null ist und die Antwort falsch ist, können Sie nicht zu dem Schluss kommen, dass Ihr UIAlertController bereits vorhanden ist. Es kann sich um einen beliebigen präsentierten ViewController handeln, z. B. einen PopOver. Mein Vorschlag, sicher zu überprüfen, ob der Alert bereits auf dem Bildschirm angezeigt wird, lautet also wie folgt (den präsentierten ViewController als UIAlertController umwandeln):

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
quelle
5

Hier ist eine Lösung, die ich in Swift 3 verwende. Diese Funktion zeigt dem Benutzer eine Warnung an. Wenn Sie sie mehrmals aufrufen, bevor der Benutzer die Warnung verworfen hat, wird der neue Warntext zu der bereits angezeigten Warnung hinzugefügt . Wenn eine andere Ansicht angezeigt wird, wird die Warnung nicht angezeigt. Nicht alle werden diesem Verhalten zustimmen, aber es funktioniert gut für einfache Situationen.

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
quelle
OK, das habe ich gebraucht. Es funktioniert auch in iOS 13.
Zoltan Vinkler
3

Wir können einfach überprüfen, ob ein View Controller vorhanden ist.

Wenn angezeigt, überprüfen Sie, ob es sich um eine Art UIAlertController handelt.

    id alert = self.presentedViewController;

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

Sie können in einer einzigen Zeile testen, ob bereits eine Warnung angezeigt wird:

if self.presentedViewController as? UIAlertController != nil {
    print ("alert already presented")
}
Thierry G.
quelle
Sie könnten den Code in Ihrer Antwort erklären. Oder wie es relevante Informationen hinzufügt, wenn bereits eine akzeptierte oder hoch bewertete Antwort vorliegt Lesen Sie, wie Sie eine gute Antwort schreiben
Léa Gris
0

Ich habe das verwendet, um zu erkennen, zu entfernen und zu alarmieren.

Zuerst erstellen wir eine Warnung mit der folgenden Funktion.

 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)

}

Und in einem anderen Teil Ihres Codes

    if yourAlert != nil {

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

    }
Hoffnung
quelle
0

Für die neueste Swift-Sprache können Sie Folgendes verwenden:

var alert = presentedViewController

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

Schließen Sie den aktuellen Controller und präsentieren Sie den Alarm-Controller wie folgt

 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
quelle
0

Swift 4.2+ Antwort

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

Für diejenigen, die nicht wissen, wie sie die meisten Viewcontroller bekommen

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+ Antwort 'keyWindow' wurde in iOS 13.0 nicht mehr empfohlen

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

Für diejenigen, die nicht wissen, wie sie die meisten Viewcontroller bekommen

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
quelle
0

Ich musste eine Warteschlange erstellen, um die UIAlertController-Anforderungen zu stapeln.

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
quelle
-3

Schließen Sie einfach den aktuellen Controller und präsentieren Sie den gewünschten, dh

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

self.displayAlertController()

Idelfonso Gutierrez
quelle