wllychng
wllychng

Reputation: 242

iOS dismiss UIAlertController in response to event

I have a situation where I want to present a UIAlertController in order to wait for an event (asynchronous request for data from third party) to finish before showing my main ViewController to the user to interact with.

Once the asynchronous code finishes, then I want to dismiss the UIAlertController. I know that normally UIAlertControllers are setup with a button to dismiss it, which is input from the user. I am wondering if what I want to do (dismiss with an event instead of user input) is possible?

So far, I tried to show the UIAlertController, and then wait in a while loop checking a boolean for when the event occurs:

var alert = UIAlertController(title: "Please wait", message: "Retrieving data", preferredStyle: UIAlertControllerStyle.Alert)

self.presentViewController(alert, animated: true, completion: nil)

// dataLoadingDone is the boolean to check   
while (!dataLoadingDone) {

}

self.dismissViewControllerAnimated(true, completion: nil)

This gives a warning

Warning: Attempt to dismiss from view controller while a presentation or dismiss is in progress!

and does not dismiss the UIAlertController. I also tried alert.dismissViewControllerAnimated(true, completion: nil) instead of self.dismissViewControllerAnimated(true, completion: nil), but this doesn't get rid of the UIAlertController either.

Upvotes: 2

Views: 3633

Answers (2)

Imanou Petit
Imanou Petit

Reputation: 92419

I wouldn't use a while loop but a didSet observer for your dataLoadingDone property. Thereby, you may try something similar to the following code:

class ViewController: UIViewController {

    var dismissAlertClosure: (() -> Void)?
    var dataLoadingDone = false {
        didSet {
            if dataLoadingDone == true {
                dismissAlertClosure?()
            }
        }
    }


    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        let alert = UIAlertController(title: "Please wait", message: "Retrieving data", preferredStyle: .Alert)
        presentViewController(alert, animated: true, completion: nil)

        // Set the dismiss closure to perform later with a reference to alert
        dismissAlertClosure = {
            alert.dismissViewControllerAnimated(true, completion: nil)
        }

        // Set boolValue to true in 5 seconds in order to simulate your asynchronous request completion
        var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(5.0 * Double(NSEC_PER_SEC)))
        dispatch_after(dispatchTime, dispatch_get_main_queue(), { self.dataLoadingDone = true })
    }

}

Upvotes: 1

wllychng
wllychng

Reputation: 242

Another user (matt) gave what I'm pretty sure is the correct answer, but he removed it, so I'm going to answer it. Just wanted to give credit (though he seems pretty reputable anyways).

What he wrote is that my UIAlertController presentation was not finished before I tried to dismiss it, so I got that error. I changed my code to the following:

// check if I need to wait at all
if (!dataLoadingDone) {
        var alert = UIAlertController(title: "Please wait", message: "Retrieving data", preferredStyle: UIAlertControllerStyle.Alert)

        self.presentViewController(alert, animated: true, completion: { () -> Void in
            // moved waiting and dismissal of UIAlertController to inside completion handler
            while (!self.dataLoadingDone) {

            }

            self.dismissViewControllerAnimated(true, completion: nil)
        })
    }

I moved the waiting and dismissal inside the completion handler of the presentViewController call, so that I know the presenting is done before dismissing. Also, I check if I need to wait at all with the first if statement, because otherwise another issue occurs if the while loop never does anything.

I'm not 100% sure yet, because my boolean is actually never false at the moment (the data retrieval happens really quickly). However, I have reason to believe it will take longer later on in my app development, so I will update the answer here once I have that.

EDIT: another user who previously posted an answer (Aaron Golden) was also correct about while loop blocking. I had thought that the while loop shares processing time with other events, but apparently not (or at least not enough time). Therefore, the above while loop DOES NOT work

Upvotes: 0

Related Questions