Assume a new iOS project, just with a navigation controller (correctly wired as entry point) and an overridden viewDidAppear() containing the following three lines of code:
self.presentViewController(UIViewController(), animated: true, completion: nil)
self.dismissViewControllerAnimated(true, completion: {})
self.presentViewController(UIViewController(), animated: true, completion: nil)
When executed, that code will raise a warning "Attempt to present UIViewController while a presentation is in progress!" when attempting to present the second controller.
Question: What exactly am I missing in order to dismiss the controller correctly before calling another controller?
You'll need to add some sort of delay on that initial presentViewController call as illustrated below:
override func viewDidAppear(animated: Bool) {
presentViewController(UIViewController(), animated: true) { () -> Void in
self.delay(0.1, closure: { () -> () in
self.dismissViewControllerAnimated(true, completion: nil)
})
}
}
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
It seems the completion block is called before the animation is truly complete.
Assuming you want the main controller to appear, present a controller, dismiss the controller, present again and dismiss then you need to chain the actions so they happen in order.
To prevent it spinning forever, you also need to only run the code when the main controller appears for the first time.
I'm no swift coder, but something like the following should work. There is a check to make sure the controller is being presented or being pushed on and then it runs the sequence using each operations completion to start the next. This guard should make sure that following each dismiss when viewDidAppear
gets called that it does not do anything on those occasions.
var firstTime = true;
func presentThenDismiss(finalCompletion: (() -> Void)?)
{
presentViewController(UIViewController(), animated: true, completion : { [weak self] Void in
// On completion of the present, we dismiss it
dispatch_async(dispatch_get_main_queue(), {
self?.dismissViewControllerAnimated(true, completion: { Void in
// On completion of the dismiss, we present another
finalCompletion!()
})
})
})
}
override func viewDidLoad() {
super.viewDidLoad()
// We only run the modal presentation code when being presented or
// being pushed on, NOT when exposed by a model dismiss or pop
//
if (firstTime){
firstTime = false;
self.presentThenDismiss { () -> Void in
self.presentThenDismiss { () -> Void in
}
}
}
}