Confirm back button on UINavigationController
Actually, you can find the Back button view and add UITapGestureRecognizer to it.
If you look at this image: Using this code:
@interface UIView (debug)
- (NSString *)recursiveDescription;
@end
@implementation newViewController
...
NSLog(@"%@", [self.navigationController.navigationBar recursiveDescription]);
You can realize how to find the View of the Back button. It is always the last one in subviews array of the navigationbar.
2012-05-11 14:56:32.572 backBtn[65281:f803] <UINavigationBar: 0x6a9e9c0; frame = (0 20; 320 44); clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x6a9ea30>>
| <UINavigationBarBackground: 0x6aa1340; frame = (0 0; 320 44); opaque = NO; autoresize = W; userInteractionEnabled = NO; layer = <CALayer: 0x6aa13b0>>
| <UINavigationButton: 0x6d6dde0; frame = (267 7; 48 30); opaque = NO; layer = <CALayer: 0x6d6d9f0>>
| | <UIImageView: 0x6d70400; frame = (0 0; 48 30); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6d6d7d0>>
| | <UIButtonLabel: 0x6d70020; frame = (12 7; 23 15); text = 'Edit'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6d6dec0>>
| <UINavigationItemView: 0x6d6d3a0; frame = (160 21; 0 0); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6d6d3f0>>
| <UINavigationItemButtonView: 0x6d6d420; frame = (5 7; 139 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x6d6d4e0>>
So I used:
UIView *backButton = [[navBar subviews] lastObject];
[backButton setUserInteractionEnabled:YES];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(alertMsg)];
[tapGestureRecognizer setNumberOfTapsRequired:1];
[backButton addGestureRecognizer:tapGestureRecognizer];
Tapping on back button and voilà:
How I worked around this situation is by setting the leftBarButtonItem
to the UIBarButtonSystemItemTrash
style (making it instantly obvious that they'll delete the draft item) and adding an alert view confirming the deletion. Because you set a custom leftBarButtonItem
it won't behave like a back button, so it won't automatically pop the view!
In code:
- (void)viewDidLoad
{
// set the left bar button to a nice trash can
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash
target:self
action:@selector(confirmCancel)];
[super viewDidLoad];
}
- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (buttonIndex)
{
// The didn't press "no", so pop that view!
[self.navigationController popViewControllerAnimated:YES];
}
}
- (void)confirmCancel
{
// Do whatever confirmation logic you want here, the example is a simple alert view
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Warning"
message:@"Are you sure you want to delete your draft? This operation cannot be undone."
delegate:self
cancelButtonTitle:@"No"
otherButtonTitles:@"Yes", nil];
[alert show];
}
It's really as simple as that! I don't see the big issue, tbh :p
I have to add a disclaimer, though; doing this breaks the default navigation behaviour and apple might not like developers doing this. I haven't submitted any apps (yet) with this feature, so I'm not sure if apple will allow your app in the store when doing this, but be warned ;)
UPDATE: Good news, everybody! In the meanwhile I've released an app (Appcident) to the App Store with this behavior in place and Apple doesn't seem to mind.
Unfortunately, you can't intercept the back button in this way. The closest facsimile is to use your own UIBarButtonItem
set to the navigationItem.leftBarButtonItem
and set an action to display your alert etc. I had a graphic designer create button images that look like the standard back button.
As an aside, I needed to intercept the back button for a different reason. I urge you to reconsider this design choice. If you are presenting a view where users can make changes and you want them to have the choice to save or cancel IMHO it's better to use 'Save' and 'Cancel' buttons vs a back button with an alert. Alerts are generally annoying. Alternatively, make it clear that the changes your users are making are committed at the time they make them. Then the issue is moot.