What is the best way to check if a UIAlertController is already presenting?

IosUialertcontroller

Ios Problem Overview


I have a tableview which, when loaded, each cell could possibly return an NSError, which I have chosen to display in a UIAlertController. Problem is I get this error in the console if multiple errors are returned.

> Warning: Attempt to present UIAlertController: 0x14e64cb00 on MessagesMasterVC: 0x14e53d800 which is already presenting (null)

Ideally, I would ideally like to handle this in my UIAlertController extension method.

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
}

Based on matt's answer, I changed the extension to a UIViewController extension, its much cleaner and saves lots of presentViewController code.

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

Ios Solutions


Solution 1 - Ios

It is not the UIAlertController that is "already presenting", it is MessagesMasterVC. A view controller can only present one other view controller at a time. Hence the error message.

In other words, if you have told a view controller to presentViewController:..., you cannot do that again until the presented view controller has been dismissed.

You can ask the MessagesMasterVC whether it is already presenting a view controller by examining its presentedViewController. If not nil, do not tell it to presentViewController:... - it is already presenting a view controller.

Solution 2 - Ios

if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {
     
      // UIAlertController is presenting.Here

}

Solution 3 - Ios

Well, the suggested solutions above has an essential problem from my point of view:

If you ask your ViewController, whether the attribute 'presentedViewController' is nil and the answer is false, you can't come to the conclusion, that your UIAlertController is already presented. It could be any presented ViewController, e.g. a popOver. So my suggestion to surely check, whether the Alert is already on the screen is the following (cast the presentedViewController as a 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")
      }
  }

}

Solution 4 - Ios

Here's a solution I use in Swift 3. It is a function that shows an alert to the user, and if you call it multiple times before the user has dismissed the alert, it will add the new alert text to the alert that's already being presented. If some other view is being presented, the alert will not appear. Not all will agree with that behavior, but it works well for simple situations.

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

Solution 5 - Ios

> Swift 4.2+ Answer

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

For those who don't know how to get top most 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' was deprecated in iOS 13.0 suggested edit

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

For those who don't know how to get top most 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
}}

Solution 6 - Ios

We can simply check if any view controller is presented.

if presented then check if it is kind of UIAlertController .

    id alert = self.presentedViewController;

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

Solution 7 - Ios

you can test - in a single line - if an alert is already presented:

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

Solution 8 - Ios

This category can auto-manage all the modal controllers include of UIAlertController.

[UIViewController+JCPresentQueue.h][1]

[1]: https://github.com/HJaycee/JCAlertController/blob/master/JCAlertController/JCAlertController/Category/UIViewController%2BJCPresentQueue.h "UIViewController+JCPresentQueue.h"

Solution 9 - Ios

I used that to detect and remove and alert.

First we create an alert with following function.

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

And in some other part of your code

    if yourAlert != nil {

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

    }

Solution 10 - Ios

For latest Swift language you can use following:

var alert = presentedViewController

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

Solution 11 - Ios

Dismiss the current controller and present the alert controller like

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

Solution 12 - Ios

I found I needed to create a queue to stack the UIAlertController requests.

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

Solution 13 - Ios

Simply dismiss the current controller and present the one you want i.e.

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

self.displayAlertController()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionhidden-usernameView Question on Stackoverflow
Solution 1 - IosmattView Answer on Stackoverflow
Solution 2 - IosBenView Answer on Stackoverflow
Solution 3 - IosLukeSideWalkerView Answer on Stackoverflow
Solution 4 - IosbiomikerView Answer on Stackoverflow
Solution 5 - IosiOS LifeeView Answer on Stackoverflow
Solution 6 - IosRaviView Answer on Stackoverflow
Solution 7 - IosThierry G.View Answer on Stackoverflow
Solution 8 - IosJayceeView Answer on Stackoverflow
Solution 9 - IosHopeView Answer on Stackoverflow
Solution 10 - IosShahid AslamView Answer on Stackoverflow
Solution 11 - IosFaiz Ul HassanView Answer on Stackoverflow
Solution 12 - IosPeter B. KramerView Answer on Stackoverflow
Solution 13 - IosIdelfonso GutierrezView Answer on Stackoverflow