Attempt to present UIAlertController on View Controller which is already presenting (null) [Swift]

我只是一个虾纸丫 提交于 2019-12-05 05:52:32
Thiha Aung

The problem is really simple, you are trying to display another UIAlertController on the currently presented UIAlertController.

So, how to solve such a case?

  1. You need to get a list of all UIAlertController's you use in your current view controller.

  2. You have to check the logic for displaying alerts in your current view controller (or other view controllers if you are doing async requests).

  3. Your code must be like this when you want to display one alert on top of another.

Assume loadingAlert is currently displaying on the screen:

self.loadingAlert.dismiss(animated: true, completion: {
     let anotherAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
     let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
     anotherAlert.addAction(okAction)
     self.present(anotherAlert, animated: true, completion: nil)
})

You have to dismiss the first one before the next one can appear. I made this answer for dismissing an alert without buttons on it to make it more efficient.

So, what about the alert with action buttons?

It will dismiss automatically when you click one of the action buttons on UIAlertController that you created.

But, if you are displaying two UIAlertControllers which include UIButtons at the same time, the problem will still occur. You need to re-check the logic for each, or you can handle it in the handler for each action :

self.connectionErrorAlert.dismiss(animated: true, completion: {
     let anotherAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
     let okAction = UIAlertAction(title: "OK", style: .default, handler: {action in
            let nextAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
            self.present(nextAlert, animated: true, completion: nil)
     })
     anotherAlert.addAction(okAction)
     self.present(anotherAlert, animated: true, completion: nil)
})

For an Answer to Mike :

DispatchQueue.main.async(execute: {

      if self.presentedViewController == nil {
           print("Alert comes up with the intended ViewController")
           var inputTextField = UITextField()

           let textPrompt = UIAlertController(title: "Test", message: "Testing", preferredStyle: .alert)

           textPrompt.addAction(UIAlertAction(title: "Continue", style: .default, handler: {
               (action) -> Void in
               // if the input matches the required text

               let str = inputTextField.text
               if str == requireTextInput {
                    print("right")
               } else {
                    print("wrong")
               }

           }))

           textPrompt.addTextField(configurationHandler: {(textField: UITextField!) in
                textField.placeholder = ""
                inputTextField = textField

            })
            weakSelf?.present(textPrompt, animated: true, completion: nil)
      } 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 _ : UIAlertController = thePresentedVC as? UIAlertController {
                      print("Alert not necessary, already on the screen !")

              } else {

                      print("Alert comes up via another presented VC, e.g. a PopOver")
              }
            }
       }
})

Thanks to @Luke Answer : https://stackoverflow.com/a/30741496/3378606

in ViewDidLoad,
Make weak variable, like weak var weakSelf = self

in NotificationCenter,
present textPropmt like

weak var weakSelf = self
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot, object: nil, queue: OperationQueue.main, using:
        { notification in
 DispatchQueue.main.async(execute: {
  //create textPrompt here in Main Thread
  weakSelf.present(textPrompt, animated: true, completion: nil)
 })
})

Duplicated from my answer here

In my situation, I was not able to put mine in a class override. So, here is what I got:

let viewController = self // I had viewController passed in as a function,
                          // but otherwise you can do this


// Present the view controller
let currentViewController = UIApplication.shared.keyWindow?.rootViewController
currentViewController?.dismiss(animated: true, completion: nil)

if viewController.presentedViewController == nil {
    currentViewController?.present(alert, animated: true, completion: nil)
} else {
    viewController.present(alert, animated: true, completion: nil)
}

I've suffered with this problem and tracked it down to this. Here is a simple app with two buttons on it. Tapping the first button causes the "2019-03-05 16:58:04.094541-0500 ReadJason[41100:1610082] Warning: Attempt to present on which is already presenting " error.

The issue was caused by copying button 2 to make button 1. Each button is tied to an action (btn1, and btn2). When I copied btn2 to make btn1, the tie to btn2 was included in the code of btn1. I then added the tie to btn1 and this resulted in two send events being tied to btn1 - and this is what causes the error.

Inspecting the events to button 1 reveals the two actions:screen shot showing the two actions. Removing the unwanted action clears the error.

screen shot of main.storyboard

<pre><code>    
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *btn1;
@property (weak, nonatomic) IBOutlet UIButton *btn2;
@end

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
}

- (IBAction)btn1:(id)sender {
   UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Btn1"
                   message:@"This is Btn1." preferredStyle:UIAlertControllerStyleAlert];

   UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}];

   [alert addAction:defaultAction];
   [self presentViewController:alert animated:YES completion:nil];
}

- (IBAction)btn2:(id)sender {
   UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Btn2"
                     message:@"This is Btn2." preferredStyle:UIAlertControllerStyleAlert];

   UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}];

   [alert addAction:defaultAction];
   [self presentViewController:alert animated:YES completion:nil];
}
</code></pre>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!