UINavigationController's documentation contains no pushViewController
methods with a completion:
parameter.

- 6,295
- 5
- 47
- 64

- 213
- 1
- 2
- 8
-
I got ur question but there is no way while push but present will have one like u expect or else u can do it didappear of pushed viewController – CoolMonster Feb 13 '14 at 04:16
6 Answers
The better solution would be wrapping the push animation by an CATransaction and set the completionBlock. There's no need to deal with timings.
[CATransaction begin];
[CATransaction setCompletionBlock:^{
//whatever you want to do after the push
}];
[[self navigationController] pushViewController:viewController animated:YES];
[CATransaction commit];

- 6,332
- 9
- 48
- 78

- 977
- 7
- 9
-
-
1At least using "slow animations" suggests that this isn't the right approach. – tcurdt Jun 15 '16 at 13:44
-
What about Swift 4? I couldn't find `CATransaction.begin()` method. – Danh Huynh Oct 28 '17 at 08:41
-
Also didn't work for me. Completion block is being executed before the animation has ended. – Kai Aug 14 '18 at 11:04
justMartin's answer worked great for me. For those new to using swift API:
CATransaction.begin()
navigationController?.pushViewController(viewController, animated: true)
CATransaction.setCompletionBlock({
//your post animation logic
})
CATransaction.commit()

- 27,874
- 70
- 431
- 719

- 660
- 9
- 29
-
-
This seems to fail for me in case there is an animation that is started off as soon as the new viewcontroller loads. For example an activity indicator. I ended up creating properties in the new view controller and setting them before pushing, instead of trying to call a method in the new view controller from the old view controller once it is pushed. – humblePilgrim Jun 08 '19 at 05:20
Solution with CATransaction
is great but there is another way to do that. You can make your controller a delegate of UINavigationController
and implement didShowViewController
method:
class FooBarController: UIViewController, UINavigationControllerDelegate {
func viewDidLoad() {
self.navigationController?.delegate = self
}
func navigationController(navigationController: UINavigationController, didShowViewController viewController: UIViewController, animated: Bool) {
// your awesome completion code here
}
}
This approach can be more convenient for your task

- 303
- 3
- 9
Not sure if I understand correctly, but I pushed a view controller in a completion block as follows. In a table view controller, added the following line to the header file:
typedef void(^myCompletion)(BOOL);
Then in the class itself, added the following method:
-(void) myMethod:(myCompletion) compblock
{
compblock(YES);
}
Now in didSelectRowAtIndexPath
, I called myMethod
and in the completion block, pushed a view controller.
[self myMethod:^(BOOL finished) {
if(finished){
dispatch_async(dispatch_get_main_queue(), ^{
DVSecondTableViewController *vc = [[DVSecondTableViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
});
}
}];
I'm not sure if it's okay to push view controllers outside of the main thread, so I embedded that call in a dispatch_async()
.

- 4,704
- 1
- 33
- 57
-
the OP is asking how to send completion block to push ,not push the viewController from completion block. Just like presentViewController has completion block. – santhu Feb 13 '14 at 04:41
Try using -[UIViewControllerTransitionCoordinator animateAlongsideTransitionInView:animation:completion:]
:
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (!self.isPushing) {
self.pushing = YES;
[super pushViewController:viewController animated:animated];
if (!self.transitionCoordinator) {
self.pushing = NO;
} else {
[self.transitionCoordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
self.pushing = NO;
}];
}
}
}

- 814
- 10
- 16
I would go with @justMartin's answer . But below is another approach.
push animation take 0.3 seconds.So, if you want to run some function here after completion use performSelector after 0.3 seconds of delay.
[self performSelector:@selector(completedPushing:) withObject:nil afterDelay:.3];
OR
After pushing the new viewController
onto navigation stack
,the old viewController view
is removed from view hierarchy.so this results in call of viewDidDisappear
on old ViewController.You can write your completion
code there. don't forget to call super
at some point of implementation of viewDidDisappear.

- 4,796
- 1
- 21
- 29
-
1You can't say that for sure. Someone's iDevice could be running slowly, thus the animation may take longer. – Supertecnoboff Nov 05 '15 at 22:14
-
The `performSelector:withObject:afterDelay:` suggestion isn't so great but the `viewDidDisappear` trick worked great for me even when it's a modal view controller that's being dismissed. Thanks for that – Adil Hussain May 02 '20 at 09:32
-