iOS segue freeze for many seconds before showing new view

In the main view of my app I have a table view and two prototype cells. I have configured the segues for each cell using the storyboard. In the view controller I override prepareForSegue to pass information on the selected cell to the destination view.

The destination view isn't particularly complex and certainly doesn't require any heavy processing to load.

THE PROBLEM

When I tap on a cell in the main controller for the very first time, the destination view appears after a long delay, from 5 to 40 seconds.


Note that:

  • If I tap on the same cell again before the destination view has appeared, this triggers the destination view to appear immediately.
  • As above, but tapping on a different cell results in the view appearing immediately but with the data from the first cell.
  • As above, but tapping on a different control (with no associated segues) triggers the destination view to appear immediately.
  • Subsequent "taps" generally manifest less delay.
  • The Time Profiler - for what I can see - shows that absolutely nothing is happening during those many seconds of delay.
  • I have tried different type of segues, but it made no difference.
  • A few println's show that the following sequence of events occurs:
    • in the main view, prepareForSegue is executed (no delays)
    • then the destination viewDidLoad is executed (no delays)
    • ... long delay ...
    • the collection and table views in the destination controller start invoking the data source related methods to fetch the data from the controller.
    • the view finally appears (with an unwanted animation, BTW, but that's a different problem)
  • So far I have only tried in the simulator.


From what I have read on this topic, I suspect the problem is potentially related to some of the above operations happening in a background thread.


In the main view controller the segues have been link using the story board (CTRL-drag the two prototype cells into the destination view).


The code looks a bit like below:


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
     var builderIndex = buildersTable.indexPathForSelectedRow()?.row

     if let destination = segue.destinationViewController as? BuilderToysListViewController
     {
          destination.botsRepository = botsRepository!
          destination.builder = builders[builderIds[builderIndex!]]
     }
}


I have made a sample project available on BitBucket


Any idea what I might be doing wrong?

Accepted Reply

You just encountered an iOS 8 bug. You can fix that the easy way or the hard way.


  1. The easy way is to not use None in your table view cell Selection setting. But this will mess up your UI.
  2. The hard way is to conform to UITableViewDelegate, assign a value to tableView.delegate and implement didSelectRowAtIndexPath using the code below.


    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            self.performSegueWithIdentifier("ToysByBuilderSegue", sender: nil)
        }
    }

Replies

You just encountered an iOS 8 bug. You can fix that the easy way or the hard way.


  1. The easy way is to not use None in your table view cell Selection setting. But this will mess up your UI.
  2. The hard way is to conform to UITableViewDelegate, assign a value to tableView.delegate and implement didSelectRowAtIndexPath using the code below.


    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            self.performSegueWithIdentifier("ToysByBuilderSegue", sender: nil)
        }
    }

Thanks. This worked for me. I added the dispatch_async...... To didSelectRowAtIndexPath as in your point 2. I don't understand point 1 where you say not to use None. No matter anyways, point 2 was excellent for me.