I am using a UITableView to present a variety of viewControllers. Depending on which index is clicked, it will present a different view controller, which can then be dismis
Check this out: https://devforums.apple.com/thread/201431
If you don't want to read it all - the solution for some people (including me) was to make the presentViewController
call explicitly on the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:myVC animated:YES completion:nil];
});
Probably iOS7 is messing up the threads in didSelectRowAtIndexPath
.
After much more debugging, I was able to determine it wasn't the view controller, but something to do with the the didSelectRowAtIndexPath and presentViewController. It started happening to other views I was presenting also, and not reliably. I also tried using if/else instead of switch with no help.
Ultimately I found this solution was able to fix it, but I'm not entirely sure why. By using the performSelector with no delay, all the views load instantly (perhaps faster than without it?) and reliably every time.
It isn't as clean of code, but it does reliably work now. Am still curious why this is needed.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch(indexPath.row){
case 0:
[self performSelector:@selector(load0:) withObject:nil afterDelay:0];
break;
case 1:
[self performSelector:@selector(load1:) withObject:nil afterDelay:0];
break;
case 2:
[self performSelector:@selector(load2:) withObject:nil afterDelay:0];
break;
...
}
}
-(void)load0:(id)sender {
[self presentViewController:self.delegateRef.zonesViewController animated:YES completion:nil];
}
-(void)load1:(id)sender {
[self presentViewController:self.delegateRef.sensorsViewController animated:YES completion:nil];
}
-(void)load2:(id)sender {
[self presentViewController:self.delegateRef.savedRidesViewController animated:YES completion:nil];
}
This is an actual bug in presentViewController:animated:completion:
. See my answer with the proper workaround here: https://stackoverflow.com/a/30787046/1812788
As already mentioned, one fix is to run any presenting of the view controllers on the main thread explicitly or to use performSelector
.
However, it wasn't clear to me why this was a fix because I was under the impression that didSelectRow
runs on the main thread anyway. It wasn't until I read this answer that things became clear for me.
For details, read the linked answer, but in short it seems that this affects cells that have a selection style of none
. This turns off the (by default grey) animation that occurs when a cell is selected which means that the run loop is not triggered for when didSelectRow
is called. So when you go to present another view controller the animation that actually presents it doesn't immediately occur on the run loop.
The solution of simply running it on the main thread is not a direct fix. You actually just need to trigger the run loop, such as by supplying it with some code and it will work. For example:
dispatch_async(dispatch_get_main_queue(), {})
[self presentViewController:viewController animated:YES completion:nil]
will also fix it. This has the side effect of triggering the run loop when the view controller is presented so the animation of the view controller now happens instantly. This is also why calling performSelector
works.
Swift 3 :
DispatchQueue.main.async(execute: {
self.present(yourViewControllerHere, animated: true, completion: nil)
})
In Swift and iOS9 it's:
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier("OpenBookingDetail", sender: self)
})