51

I'm presenting an UIAlertViewController without any buttons, as it is supposed to just inform users that uploading is in progress. The app is supposed to upload some files to Amazon S3 (some things happen on parallel threads) and I'm afraid that the reference to the alert view controller gets lost when I want to dismiss it.

Any idea on what could be wrong? I even don't know how to debug this since there's no error in the Debug area?

I have a class level property: var uploadInProgressAlert = UIAlertController()

I use this code to present my alert without buttons (it works):

self.uploadInProgressAlert = UIAlertController(title: "Uploading", message: "Please wait.", preferredStyle: .Alert)
self.presentViewController(self.uploadInProgressAlert, animated: true, completion: nil)

This code is to dismiss the alert (the alert doesn't get dismissed): self.uploadInProgressAlert.dismissViewControllerAnimated(false, completion: nil)

In this thread: iOS dismiss UIAlertController in response to event someone talked about "holding the reference". I don't know what it means "hold the reference" and I think this could be the root of the problem.

EDIT: I've put the above code in a simple test app and there it works. But when things get complicated with some parallel threads I can't find a way to dismiss the alert.

var delay4s = NSTimer()
var delay8s = NSTimer()
var alert = UIAlertController()

func showAlert() {
    if NSClassFromString("UIAlertController") != nil {
        alert = UIAlertController(title: "Uploading", message: "Please wait! \n\n", preferredStyle: UIAlertControllerStyle.Alert)
        self.presentViewController(alert, animated: true, completion: nil)
    }
}

func dismissAlert(){
    self.alert.dismissViewControllerAnimated(true, completion: nil)
}

override func viewDidLoad() {
    super.viewDidLoad()
    delay4s = NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: "showAlert", userInfo: nil, repeats: false)
    delay8s = NSTimer.scheduledTimerWithTimeInterval(8.0, target: self, selector: "dismissAlert", userInfo: nil, repeats: false)
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Andrej
  • 7,266
  • 4
  • 38
  • 57
  • 3
    holding the reference is what you're doing - make alert an instance variable – kostek Aug 06 '15 at 13:34
  • 1
    Holding a reference is exactly what you did by declaring a class property `uploadInProgressAlert`. Are you sure that the line for dismissing is ever called ? – Zell B. Aug 06 '15 at 13:34
  • Yes, I'm sure that the line gets called, because I have some println() before and after the line. – Andrej Aug 06 '15 at 13:36

5 Answers5

73

Generally the parent view controller is responsible for dismissing the modally presented view controller (your popup). In Objective-C you would do something like this in the parent view controller:

[self dismissViewControllerAnimated:YES completion:nil];

The same code in Swift versions < 3 would be:

self.dismissViewControllerAnimated(true, completion: nil)

Swift 3.0:

self.dismiss(animated: true, completion: nil)
JiuJitsuCoder
  • 1,866
  • 14
  • 13
  • 3
    Yes, this actually pointed me in the right direction. I had to use `self.dismissViewControllerAnimated(false, completion: nil)` instead of `self.uploadInProgressAlert.dismissViewControllerAnimated(false, completion: nil)`. However I don't understend why it works, but it works. – Andrej Aug 06 '15 at 14:31
  • 3
    It works because as @MySpecialPurpose the parent view controller is responsible for dismissing any presented view controller – Victor Sigler Aug 06 '15 at 14:33
  • 3
    what if the parent is also a "Modal"? – Shabarinath Pabba Sep 14 '17 at 17:48
  • @Daniel so what can be done if another VC presented itself first? – Shabarinath Pabba Sep 14 '17 at 17:50
  • Is this safe to call even if no alert controller is currently displayed? I've tried it and it just does nothing (as desired), but I'm just wondering if there's any unintended side-effects. – Extragorey Nov 16 '17 at 22:58
13

for swift you can do:

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

true will animate the disappearance, and false will abruptly remove the alert

Declan McKenna
  • 4,321
  • 6
  • 54
  • 72
sandeep jaglan
  • 139
  • 2
  • 5
9

If you want to post an alert that displays briefly, then dismisses itself, you could use the following method:

  func postAlert(title: String, message: String) {
    let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
    self.present(alert, animated: true, completion: nil)

    // delays execution of code to dismiss
    DispatchQueue.main.asyncAfter(deadline: .now() + 2.0, execute: {
      alert.dismiss(animated: true, completion: nil)
    })
  }
Adrian
  • 16,233
  • 18
  • 112
  • 180
6

Use the alertController's own method to destroy itself.

UIAlertController *alertController = [UIAlertController 
alertControllerWithTitle:...];

[alertController dismissViewControllerAnimated:YES completion:nil];
ylgwhyh
  • 1,588
  • 18
  • 21
4

Nothing above seemed to work, but here is what works for me perfectly (xcode 10, swift 5). Enjoy!

Step 1: Place this is your viewController Class

    var newQuestionAlert:UIAlertController?

Step 2: Create function to show alert

  func ShowNewQuestionPopup() {
    if newQuestionAlert == nil {
        newQuestionAlert = UIAlertController(title: "Notice", message: "Next Question Starting", preferredStyle: .alert)
        if let newQuestionAlert = newQuestionAlert {
            newQuestionAlert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
                self.newQuestionAlert = nil
                return
            }))
            self.present(newQuestionAlert, animated: true, completion: nil)
         }
     }
 }

Step 3: Create function to dismiss alert

func autoDismiss() {
    newQuestionAlert?.dismiss(animated: false, completion: nil)
    newQuestionAlert = nil
}

Step 4: Call functions as needed

Stotch
  • 373
  • 3
  • 10