detect long press on UINavigationItem's back button

Viewed 6121

I want to add functionality to my back buttons through my UINavigationController-based app where long-pressing the back button will pop to root. However, I can't figure out where to attach the gesture recognizer. Do I subclass UINavigationBar and try and detect if the long press is in the left button region?

I've heard of people adding similar functionality before. Anyone have any ideas?

4 Answers

I know this question is old, but I came up with a solution. Instead of trying to add the gesture recognizer to the button itself (which would be ideal), I added it to the self.navigationController.navigationBar and then in the action method, use the locationInView to see if I'm over the back button. I wasn't entirely sure about how to identify the back button precisely, so I'm clumsily just grabbing the the first subview with an x coordinate less than some arbitrary value, but it seems promising. If someone has a better way to identify the frame of the back button, let me know.

- (void)longPress:(UILongPressGestureRecognizer *)sender 
{
    if (sender.state == UIGestureRecognizerStateEnded)
    {
        // set a default rectangle in case we don't find the back button for some reason

        CGRect rect = CGRectMake(0, 0, 100, 40);

        // iterate through the subviews looking for something that looks like it might be the right location to be the back button

        for (UIView *subview in self.navigationController.navigationBar.subviews)
        {
            if (subview.frame.origin.x < 30) 
            {
                rect = subview.frame;
                break;
            }
        }

        // ok, let's get the point of the long press

        CGPoint longPressPoint = [sender locationInView:self.navigationController.navigationBar];

        // if the long press point in the rectangle then do whatever

        if (CGRectContainsPoint(rect, longPressPoint))
            [self doWhatever];
    }
}

- (void)addLongPressGesture
{
    if (NSClassFromString(@"UILongPressGestureRecognizer"))
    {
        UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
        [self.navigationController.navigationBar addGestureRecognizer:longPress];
        [longPress release];
    }
}

Here's my solution:

In appDelegate (the "owner" of the nav bar in my app), In applicationDidFinishLaunchingWithOptions:

Get the nav bar view and add the gesture recognizer to the whole view:

// Get the nav bar view
UINavigationBar *myNavBar = nil;
for (UIView *view in [self.window.rootViewController.view subviews]) {
    if ([view isKindOfClass:[UINavigationBar class]]) {
        NSLog(@"Found Nav Bar!!!");
        myNavBar = (UINavigationBar *)view;
    }
}

UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self
                                                                                        action:@selector(backButtonLongPress:)];
[myNavBar addGestureRecognizer:longPress];
NSLog(@"Gesture Recognizer Added.");

Then in appDelegate, in -(void) backButtonLongPress:(id) sender

Check to see if the gesture occurs within the frame of the back button:

if ([sender state] == UIGestureRecognizerStateBegan) {

    // Get the nav bar view
    UINavigationBar *myNavBar = nil;
    for (UIView *view in [self.window.rootViewController.view subviews]) {
        if ([view isKindOfClass:[UINavigationBar class]]) {
            NSLog(@"Found Nav Bar!!!");
            myNavBar = (UINavigationBar *)view;
        }
    }

    // Get the back button view
    UIView *backButtonView = nil;
    for (UIView *view in [myNavBar subviews]) {
        if ([[[view class] description] isEqualToString:@"UINavigationItemButtonView"]) {
            backButtonView = view;
            NSLog(@"Found It: %@", backButtonView);
            NSLog(@"Back Button View Frame: %f, %f; %f, %f", backButtonView.frame.origin.x, backButtonView.frame.origin.y, backButtonView.frame.size.width, backButtonView.frame.size.height);
        }
    }

    CGPoint longPressPoint = [sender locationInView:myNavBar];
    NSLog(@"Touch is in back button: %@", CGRectContainsPoint(backButtonView.frame, longPressPoint) ? @"YES" : @"NO");
    if (CGRectContainsPoint(backButtonView.frame, longPressPoint)) {
        // Place your action here
    }

    // Do nothing if outside the back button frame

}
Related