Checking if a UIViewController is about to get Popped from a navigation stack?

41,302

Solution 1

I don't think there is an explicit message for this, but you could subclass the UINavigationController and override - popViewControllerAnimated (although I haven't tried this before myself).

Alternatively, if there are no other references to the view controller, could you add to its - dealloc?

Solution 2

Override the viewWillDisappear method in the presented VC, then check the isMovingFromParentViewController flag within the override and do specific logic. In my case I'm hiding the navigation controllers toolbar. Still requires that your presented VC understand that it was pushed though so not perfect.

Solution 3

Fortunately, by the time the viewWillDisappear method is called, the viewController has already been removed from the stack, so we know the viewController is popping because it's no longer in the self.navigationController.viewControllers

Swift 4

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    if let nav = self.navigationController {
        let isPopping = !nav.viewControllers.contains(self)
        if isPopping {
            // popping off nav
        } else {
            // on nav, not popping off (pushing past, being presented over, etc.)
        }
    } else {
        // not on nav at all
    }
}

Original Code

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    if ((self.navigationController) && 
        (![self.navigationController.viewControllers containsObject:self])) {
        NSLog(@"I've been popped!");
    }
}

Solution 4

Try overriding willMoveToParentViewController: (instead of viewWillDisappear:) in your custom subclass of UIViewController.

Called just before the view controller is added or removed from a container view controller.

- (void)willMoveToParentViewController:(UIViewController *)parent
{
    [super willMoveToParentViewController:parent];
    if (!parent) {
        // `self` is about to get popped.
    }
}

Solution 5

This is working for me.

- (void)viewDidDisappear:(BOOL)animated
{
    if (self.parentViewController == nil) {
        NSLog(@"viewDidDisappear doesn't have parent so it's been popped");
        //release stuff here
    } else {
        NSLog(@"PersonViewController view just hidden");
    }
}
Share:
41,302
Jasarien
Author by

Jasarien

iPhone, iPad, Mac OS X Software Engineer based in London, UK. Currently Senior Mobile Software Engineer at Rantmedia. Working on Provenance, a multi-system retro console emulator, in my free time as a labour of love. Blog Twitter Github Provenance

Updated on October 25, 2020

Comments

  • Jasarien
    Jasarien over 3 years

    I need to know when my view controller is about to get popped from a nav stack so I can perform an action.

    I can't use -viewWillDisappear, because that gets called when the view controller is moved off screen for ANY reason (like a new view controller being pushed on top).

    I specifically need to know when the controller is about to be popped itself.

    Any ideas would be awesome, thanks in advance.

  • Max Steinmeyer
    Max Steinmeyer about 15 years
    The dealloc will only be called after the pop, though, not before.
  • Jasarien
    Jasarien about 15 years
    I don't think that's the best solution. I want to use this controller in other places in the app, and the behaviour I want to execute is specific to this controller and has to happen when the controller is popped. I don't want to have to subclass every navController this viewController appears in.
  • Jasarien
    Jasarien about 15 years
    It seems that this is best solution. It seems to work, so thanks!
  • Alex
    Alex about 15 years
    Try this: subclass UIViewController, override popViewController:animated: and send a custom message to the UIViewController's delegate. Then, the delegate can decide what it needs to do in each case.
  • grahamparks
    grahamparks over 13 years
    This is exactly what I needed. Thanks.
  • mxcl
    mxcl over 13 years
    My experimentation suggests that actually [UINavigationController visibleViewController] is already set to YourAboutToAppearController. Though indeed the animation has yet to start.
  • Jasarien
    Jasarien about 13 years
    An interesting idea and approach, but I fear it may be slightly too fragile. It relies on an implementation detail that could change at any time.
  • Oded Ben Dov
    Oded Ben Dov about 13 years
    Agreed, hence that last skepticism.
  • reflog
    reflog almost 13 years
    Thanks Oded, that little snippet helped quite alot!
  • David H
    David H over 12 years
    This is a great solution and not fragile at all as other suggestions. One could also use a Notification so anyone wanting to know about popped views could listen in.
  • HelmiB
    HelmiB over 12 years
    Subclassing 'UINavigationController' will lead app to be rejected by apple. documentation
  • Zac Bowling
    Zac Bowling over 12 years
    Subclass will not get you rejected by apple. Class is just not intended for subclassing because apple uses instances of NSNavigaionController that can't get access too, but there is inherently with subclassing.
  • flypig
    flypig over 11 years
    Yes, this would be a good answer, super fast, without delegate, without notification.... thanks. Adding the logic to the viewDidDisapper is not perfect, for example, when pushing or presenting another view controller inside it, the viewDidDisAppear will be invoked too.... This is why I really like this option.
  • flypig
    flypig over 11 years
    Actually, subclass will be a better choice, or there will be a warning, but you can surpress it via: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation" .......... #pragma clang diagnostic pop
  • johndpope
    johndpope over 11 years
    also there's a side effect with full screen uipopovercontrollers or modal view controllers appearing and triggering these.
  • Ethan
    Ethan about 11 years
    This is a clean solution in iOS 5+, and who isn't on iOS 5 at this point?
  • Jessedc
    Jessedc about 11 years
    Using the UINavigationControllerDelegate seems like a better option than subclassing UINavigationController.
  • Joshua
    Joshua almost 11 years
    Definitely the better answer here and one that works at the current time. Removes the need for subclassing, which whilst handy may be a bit over the top for some.
  • Jakob Egger
    Jakob Egger almost 11 years
    Calling respondsToSelector is unnecessary. popToRootViewControllerAnimated: is supported by every UINavigationController.
  • Jakob Egger
    Jakob Egger almost 11 years
    Also, the predicate test is bad. It only checks if a controller with the same class is in the list, not if this specific controller is there. It would be better to use something simpler like: [self.navigationController.viewControllers containsObject:self]
  • caoimghgin
    caoimghgin almost 11 years
    Jakob Egger is spot on. I've updated code per his suggestions.
  • Pei
    Pei over 10 years
    From Apple doc. "... For example, a view controller can check if it is disappearing because it was dismissed or popped by asking itself in its viewWillDisappear: method by checking the expression ([self isBeingDismissed] || [self isMovingFromParentViewController])"
  • hariseldon78
    hariseldon78 about 10 years
    @Alex UIViewController does not have the popViewController:animated: method.
  • tsafrir
    tsafrir about 10 years
    Thanks @Pei for this comment. I will appreciate if you could add a link to this Apple doc.
  • Pei
    Pei almost 10 years
    It's actually from inside iOS SDK documentation. You can find this in line 229 to 232 of UIViewController.h as of Xcode 5.1.1.
  • Ayush Goel
    Ayush Goel over 9 years
    Lines have changed to 270-275 as of Xcode 6.1.1 cc: @Pei
  • amergin
    amergin over 9 years
    Thanks Caoimhghin (and a fada over the last i to be precise) (pron: kwee-veen) - although I think I might use MattDiPasquale's override as it's a bit simpler
  • Bamaco
    Bamaco almost 9 years
    Sounds like this is the way to go! Can't wait to try this. +1
  • Bamaco
    Bamaco almost 9 years
    Indeed! why would anyone use viewDidDisappear when it is so much less reliable than willMoveToParentViewController: Using iOS 8.4, I think this should be the accepted answer.
  • ObjSal
    ObjSal over 8 years
    Another good thing about this method is that view controllers still has reference to the navigation controller (if it has one)
  • D6mi
    D6mi over 7 years
    Would like to add that this is not as bulletproof as I first thought. Instead of overriding "willMoveToParentViewController", you should override "didMoveToParentViewController" with the same code inside. The reasoning behind this is that "willMoveToParentViewController" will fire-off even if the user did not COMPLETE the pop using the interactive gesture - you will get a false positive; on the other hand, "didMoveToParentViewController" will not fire until the full transition is complete.
  • Orkhan Alikhanov
    Orkhan Alikhanov over 7 years
    If anyone have freezing issues after using above code, just leave a comment.
  • Anthony Mills
    Anthony Mills almost 7 years
    You may need to check isMovingFromParentController and isBeingDismissed both for the current view controller and all parents of the current view controller.
  • Roi Mulia
    Roi Mulia over 6 years
    Should I have this issue? I don't see it now, but is it a possible bug?
  • Orkhan Alikhanov
    Orkhan Alikhanov over 6 years
    @RoiMulia I had it during swipe gesture. In iOS 9.3.3. Check if you see that issue during swipe.
  • Roi Mulia
    Roi Mulia over 6 years
    Thanks, I'll check it closely
  • Roi Mulia
    Roi Mulia over 6 years
    It's not freezing, but it's not calling UINavigationController popViewController method.. Did you fixed it?
  • Orkhan Alikhanov
    Orkhan Alikhanov over 6 years
    @RoiMulia Hey, I updated the answer with the code I used. Seems that I had added a comment when I wrote the code.
  • SimonTheDiver
    SimonTheDiver about 6 years
    also this fires when the controller appears as well as disappears
  • Simon Moshenko
    Simon Moshenko over 4 years
    Thank you, your answer really helped me, but it did not work right out of box so I have changed it, and will post another answer.
  • Muhammadjon
    Muhammadjon almost 3 years
    @Jasarien On the method viewWillDisappear, One can use self.isMovingFromParent variable to check weather it is being popped or not.