9

I need to do certain things when my view controller is both pushed or popped from the navigation stack, but don't want to use viewillappear / viewdidappear or viewwilldisappear / viewdiddisappear since those cover cases besides when the view controller is pushed / popped. Is the correct way to go about this to use the navigationcontroller delegate and the navigationController:didShowViewController:animated: and navigationController:willShowViewController:animated: ? If not, how is the best way to go about this?

Ser Pounce
  • 14,196
  • 18
  • 84
  • 169

8 Answers8

8

To find out when it's pushed, you can use the

UINavigationControllerDelegate

and implement

- (void)navigationController:(UINavigationController *)navigationController 
      willShowViewController:(UIViewController *)viewController
                    animated:(BOOL)animated

This method will fire whenever the viewcontroller is pushed into the navigation stack, and whenever the viewcontroller on top of it is popped off, thus revealing it again. So you have to use a flag to figure out if it's been initialized yet, if it hasn't means it just was pushed.

To find out when it's been popped, use this answer:

viewWillDisappear: Determine whether view controller is being popped or is showing a sub-view controller

Community
  • 1
  • 1
Ser Pounce
  • 14,196
  • 18
  • 84
  • 169
6

You can try UINavigationController delegate methods it calls when object push or pop from navigation controller stack.

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
iOSPawan
  • 2,884
  • 2
  • 25
  • 50
3

Here's an example to detect when a view controller is being pushed onto the navigation stack by overriding -viewWillAppear: and popped by overriding -viewWillDisappear:

-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
    if (self.isMovingToParentViewController) {
        NSLog(@"view controller being pushed");        
    }
}

-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
    if (self.isMovingFromParentViewController) {
        NSLog(@"view controller being popped");
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
MobileDev
  • 3,750
  • 4
  • 32
  • 35
1

You can always create a simple subclass of the UINavigationController and wrap its superclass's methods so that you set a flag before calling them:

ActionNavigationController.h

#import <UIKit/UIKit.h>

@interface ActionNavigationController : UINavigationController
@property (nonatomic, readonly) BOOL pushing;
@end

ActionNavigationController.m

#import "ActionNavigationController.h"

@implementation ActionNavigationController
@synthesize pushing = _pushing;

-(void)pushViewController:(UIViewController *)viewController 
                 animated:(BOOL)animated {
    _pushing = YES;
    [super pushViewController:viewController animated:animated];
}

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {
    _pushing = NO;
    return [super popViewControllerAnimated:animated];
}
- (NSArray *)popToViewController:(UIViewController *)viewController 
                        animated:(BOOL)animated {
    _pushing = NO;
    return [super popToViewController:viewController animated:animated];
}
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated {
    _pushing = NO;
    return [super popToRootViewControllerAnimated:animated];
}

@end

As pushing will evaluate to NO event if nothing is happening, this code is expected to be accessed from the UINavigationControllerDelegate.

Mike M
  • 4,879
  • 5
  • 38
  • 58
1

Be careful to use

- (void)navigationController:(UINavigationController *)navigationController 
  willShowViewController:(UIViewController *)viewController
                animated:(BOOL)animated

If user swipe from edge into right for popping view controller(and not actually pop it), it will invoke above delegate's function but not below function

- (void)navigationController:(UINavigationController *)navigationController 
   didShowViewController:(UIViewController *)viewController 
                animated:(BOOL)animated;

Please refer to https://gist.github.com/nonamelive/9334458

Hsiao-Ting
  • 3,456
  • 2
  • 15
  • 20
0

You can do something like that in willShowViewController

func navigationController(navigationController: UINavigationController, willShowViewController viewController: UIViewController, animated: Bool) {
    if self.navigationController!.viewControllers.contains(self){
        print ("push")
    } else {
        print ("pop")
    }
}
d.rozumeenko
  • 126
  • 2
  • 3
0
  • (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
  • (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

these delegate functions arent invoked on iOS13 devices (compiled with Xcode 11). Is this a bug for now?

Kaan Esin
  • 70
  • 7
0

It's bad practice to set the delegate to something inside the navigation controller's hierarchy, instead observe the notifications:

UINavigationControllerWillShowViewControllerNotification

UINavigationControllerDidShowViewControllerNotification

And the userInfo object contains the "NextVisible" and "LastVisible" view controllers. However using isMoving in appearance methods is probably the best way.

malhal
  • 26,330
  • 7
  • 115
  • 133