Swift: Threading Issue With DismissViewController() and Dismissing Keyboard

1.3k Views Asked by At

I'm send some data up to my server, and upon a response, I dismiss my view controller. In the viewController's viewWillDisappear() I attempt to dismiss the keyboard.

If the presentER viewController is not the root view controller, I receive the exception "[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'". I have tried to call a function that dismissed the keyboard then the viewcontroller (removing the need for viewwilldisappear()) but it has the same issue.

Code: //within presented viewcontroller

override func viewWillDisappear(animated: Bool)
    {
            super.viewWillDisappear(animated)
            self.view.endEditing(true)
    }

Code: //within presentER viewcontroller

func manage_response(//)
    {
            run_on_background_thread
            {
                    self.parse(//)
                    run_on_main_thread
                    {
                          self.presented_controller.dismissViewControllerAnimated(true, completion: nil)

                    }
            }
    }

Everything I've seen has indicating this is how to handle the situation. The problem only exists when I have a keyboard displayed and am trying to dismiss it with it's respective viewcontroller.

Edit:

Syntax for trailing method functions I use for threading:

func run_on_background_thread(code: () -> Void)
{
           dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), code)
}

func run_on_main_thread(code: () -> Void)
{
        dispatch_async(dispatch_get_main_queue(), code)
}

Solution:

I've tracked the issue down. The case is different than earlier thought. This occurs when I am trying to display a UIAlertController (via presentViewController) because the query did not return proper data / credentials.

run_on_main_thread
            {
                    if let controller = visibleViewController() //recursive
                    {
                            controller.view.endEditing(true)
                            controller.presentViewController(alert, animated: true, completion: nil)
                    }
            }
3

There are 3 best solutions below

0
On BEST ANSWER

The problem was displaying a UIAlertViewController. This logic/code will resolve such issue:

run_on_main_thread
{
    if let controller = visibleViewController() //recursive
    {
         controller.view.endEditing(true)       
         controller.presentViewController(alert, animated: true, completion: nil)
    }
}
3
On

You should try to dismiss it in main queue this way :

dispatch_async(dispatch_get_main_queue()) { self.presented_controller.dismissViewControllerAnimated(true, completion: nil) }

4
On

You've got the right general idea, but what you've posted is pseudo-code. The devil is in the details. If you are getting that error message then somewhere your code is making UIKit calls from a background thread.

NSThread has a class method isMainThread(). You can use that to check to see what code is being run from the background.