When dismissing a modal view controller using dismissViewController
, there is the option to provide a completion block. Is there a similar equivalent for
The completion block is called after the viewDidDisappear method is called on the presented view controller, So putting code in the viewDidDisappear method of the popped view controller should work the same as a completion block.
I achieved exactly this with precision using a block. I wanted my fetched results controller to show the row that was added by the modal view, only once it had fully left the screen, so the user could see the change happening. In prepare for segue which is responsible for showing the modal view controller, I set the block I want to execute when the modal disappears. And in the modal view controller I override viewDidDissapear and then call the block. I simply begin updates when the modal is going to appear and end updates when it disappears, but that is because I'm using a NSFetchedResultsController however you can do whatever you like inside the block.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"addPassword"]){
UINavigationController* nav = (UINavigationController*)segue.destinationViewController;
AddPasswordViewController* v = (AddPasswordViewController*)nav.topViewController;
...
// makes row appear after modal is away.
[self.tableView beginUpdates];
[v setViewDidDissapear:^(BOOL animated) {
[self.tableView endUpdates];
}];
}
}
@interface AddPasswordViewController : UITableViewController<UITextFieldDelegate>
...
@property (nonatomic, copy, nullable) void (^viewDidDissapear)(BOOL animated);
@end
@implementation AddPasswordViewController{
...
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
if(self.viewDidDissapear){
self.viewDidDissapear(animated);
}
}
@end
There is no way to do what you're wanting out-of-the-box. i.e. there is no method with a completion block for popping a view controller from a nav stack.
What I would do is put the logic in viewDidAppear
. That will be called when the view has finished coming on screen. It'll be called for all different scenarios of the view controller appearing, but that should be fine.
Or you could use the UINavigationControllerDelegate
method navigationController:didShowViewController:animated:
to do a similar thing. This is called when the navigation controller has finished pushing or popping a view controller.
Based on @HotJard's answer, when all you want is just a couple of lines of code. Quick and Easy.
Swift 4:
_ = self.navigationController?.popViewController(animated: true)
self.navigationController?.transitionCoordinator.animate(alongsideTransition: nil) { _ in
doWhatIWantAfterContollerHasPopped()
}
Swift 3 answer, thanks to this answer: https://stackoverflow.com/a/28232570/3412567
//MARK:UINavigationController Extension
extension UINavigationController {
//Same function as "popViewController", but allow us to know when this function ends
func popViewControllerWithHandler(completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.popViewController(animated: true)
CATransaction.commit()
}
func pushViewController(viewController: UIViewController, completion: @escaping ()->()) {
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
self.pushViewController(viewController, animated: true)
CATransaction.commit()
}
}
Swift 4 version with optional viewController parameter to pop to a specific one.
extension UINavigationController {
func pushViewController(viewController: UIViewController, animated:
Bool, completion: @escaping () -> ()) {
pushViewController(viewController, animated: animated)
if let coordinator = transitionCoordinator, animated {
coordinator.animate(alongsideTransition: nil) { _ in
completion()
}
} else {
completion()
}
}
func popViewController(viewController: UIViewController? = nil,
animated: Bool, completion: @escaping () -> ()) {
if let viewController = viewController {
popToViewController(viewController, animated: animated)
} else {
popViewController(animated: animated)
}
if let coordinator = transitionCoordinator, animated {
coordinator.animate(alongsideTransition: nil) { _ in
completion()
}
} else {
completion()
}
}
}