Nate Uni
Nate Uni

Reputation: 943

Attempt to present ViewController which is already presenting (null)

I am creating a user and logging in the user after using the Facebook SDK. All is as it should be with correct inserting the user into the database. I then want to present an alert and upon dismissal of the alert, I want to load a different storyboard (essentially the main menu).

Now my understanding is the error is an issue with threading. BUT.. I am passing the loading of the storyboard etc in as a block to the alert. So shouldn't it then get called only after the user dismisses the alert? I also tried making it run on the main thread (which was mentioned here) but I am still getting the error.

What am I missing here?

The originating call:

if DBUser.insertDictionary(user.getModelAsStringDictionary()) {
    if DBUser.loginUserWithFacebookId(facebookId!){
        self.accountCreatedAlert()
    }else{
        //log error, user could not be logged in.
    }
    }else{
        // log error user account could not be inserted into the database
    }
}

The async bundling of the alert text and the switchToMainStoryboard() method:

private func accountCreatedAlert(fn:()){
    let asyncMoveToMain = {dispatch_async(dispatch_get_main_queue()) {
        self.switchToMainStoryboard()
    }}
    self.showAlert("You will now be logged in", title: "Account created", fn: asyncMoveToMain)
}

The Alert:

func showAlert(text : NSString, title : NSString, fn:()->Void){
    var alert = UIAlertController(title: title, message: text, preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
    UIApplication.sharedApplication().delegate?.window!?.rootViewController?.presentViewController(alert, animated: true, completion: fn)
}

And where the storyboard gets loaded:

func switchToMainStoryboard() ->Void{
    let main = UIStoryboard(name: "Main", bundle: nil)
    var viewController = main.instantiateInitialViewController() as UIViewController
    viewController.modalTransitionStyle = UIModalTransitionStyle.CoverVertical
    self.presentViewController(viewController, animated: true, completion: nil)
}

Upvotes: 1

Views: 10864

Answers (2)

Sardek
Sardek

Reputation: 1

Alternative way is that you can ask GCD to delay with your next presentation, like so:

    let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
    // next present
}

Upvotes: -2

Nate Uni
Nate Uni

Reputation: 943

So I tried a lot of messing about with the dispatch_async and dispatch_sync methods and each time, no matter what I did I was getting a lock condition that froze the UI.

Upon further inspection of the documentation (and my code), I provided the block instead to the AlertControllerAction instead of the presentViewController. Thanks to this post for providing the syntax. The resulting changes are as follows:

// MARK: Alert Related Methods
func showAlert(text : NSString, title : NSString, fn:()->Void){
    var alert = UIAlertController(title: title, message: text, preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: {(alert: UIAlertAction!) in fn()}))
    UIApplication.sharedApplication().delegate?.window!?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
}

And then went about calling it like so:

private func showAccountCreatedAlertThenLoadMainStoryboard(){
    self.showAlert("You will now be logged in", title: "Account created", fn: {self.switchToMainStoryboard()})
}

The required sequence is now happening without issue. Hope this helps someone else.

Upvotes: 0

Related Questions