8

How come the completion block for this CATransaction never fires?

[CATransaction begin];
[CATransaction setCompletionBlock:^{
    // table animation has finished
    NSLog(@"why does this section never execute?");
}];
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.currentFeedItems.count inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
[CATransaction commit];

The tableview animation works, but the completion block is never executed. The Apple documentation says that the completion block is guaranteed to execute.

Darren
  • 1,359
  • 11
  • 22
  • Can you let us know where / when you call this, because it is working on my side. – Unheilig Dec 14 '14 at 14:36
  • It is called in response to a button press – Darren Dec 14 '14 at 15:17
  • Can you update your question with the details? – Unheilig Dec 14 '14 at 15:20
  • I originally set up a button in a table cell and used a delegate to get the controller to call the above code. Didn't work. So then, thinking it may have something to do with the thread it was executing on I wrapped the above code in a dispatch_async(dispatch_get_main_queue()... block. Didn't work. So then, I instead used a notification to fire the above code. Didn't work. So... finally I tried putting the above code in a simple method on the controller itself and, when didSelectRowAtIndexPath fired, calling this method. That didn't work either. – Darren Dec 14 '14 at 15:56

1 Answers1

15

Oh boy, this was so obscure I'm actually amazed I found the problem.

The cell that was being reloaded contains a UIActivityIndicatorView, which worked fine. When the cell reloads it implicitly redraws the table cell and a startAnimating call happens within that process in the table cell. Somehow that startAnimating call interferes with the CATransaction completion block.

When I wrap the startAnimating call in a dispatch_async block, the problem goes away:

dispatch_async(dispatch_get_main_queue(), ^{
    [self.loadingInd startAnimating];
});
Darren
  • 1,359
  • 11
  • 22
  • 1
    thanks for including your solution. I had a CATransaction block with a completion block that has been working forever. At some point in my latest release cycle it just.... stopped being called. Wrapping in dispatch_async appears to have fixed it. (though it pains me to do it without understanding *why* it fixed it!) – Eli Burke Jul 25 '16 at 15:54
  • oh my god, I have no idea why this works, but it works on my end too.... – SudoPlz Aug 02 '19 at 14:40
  • 1
    This https://stackoverflow.com/a/37928557/1658268 explains a bit further why that may be happening – SudoPlz Aug 06 '19 at 21:43
  • 1
    My guess is that `[CATransaction setCompletionBlock:]` enqueues some work on the main queue that takes care of setting the completion, so by wrapping `popViewControllerAnimated:` in a dispatch async we’re waiting for that work to complete before triggering the animation. That has to be why it works. – SudoPlz Aug 07 '19 at 14:19