UINavigationController: How to cancel the back button event?
In my UIViewController
I have a UINavigationController
with a default back button. When the user clicks the back button, a warning message should appear: "Do you really want to go back?". I know, that it is not possible to trap the back button event. It's only possible the use viewWillDisappear
and set a flag:
- (void)viewWillDisappear:(BOOL)animated {
if (backBtnPressed) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"Question" message:@"Do you really want to go back?" delegate:self cancelButtonTitle:@"No" otherButtonTitles: @"Yes", nil] autorelease];
[alert show];
}
}
- (void)alertV开发者_开发知识库iew:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
// don't go back!
// cancel the back button event
}
else if (buttonIndex == 1) {
// go back
}
}
But with this code I have no chance! I can't stop the back button event, isn't it?
Do I have to write my own back button and set it as leftBarButtonItem
? Or is there anybody with a great idea? :-)
Thanks for your help!
My answer from another thread matches this question. So I repost it here:
I've implemented UIViewController-BackButtonHandler extension. It does not need to subclass anything, just put it into your project and override navigationShouldPopOnBackButton
method in UIViewController
class:
-(BOOL) navigationShouldPopOnBackButton {
if(needsShowConfirmation) {
// Show confirmation alert
// ...
return NO; // Ignore 'Back' button this time
}
return YES; // Process 'Back' button click and Pop view controller
}
Download sample app.
What you need to do is to use the delegate of the navigation bar, and not the navigation controller.
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item; // called to push. return NO not to.
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item; // called at end of animation of push or immediately if not animated
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item; // same as push methods
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item;
viewWillDisappear
is a delegate method for the event that the view is going to disappear - and there's nothing the developer can do about that! If you could, it would be a viewShouldDisappear
delegate method.
So I guess the only way is as you suggest, to use a custom leftBarButtonItem
.
I must say this is one of the common use cases that Apple doesn't seem to make easy, and I see a lot of effort trying to get this working. I thought maybe I should summarize my findings here.
As many have pointed out, the method below in UINavigationBarDelegate
is key to implementing this feature.
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
Many have subclassed UINavigationController
and implemented the method above to make it easy to use without direct access to the UINavigationBar
.
Unfortunately, there still remain some issues.
- The swipe back gesture does not invoke this method.
- Although it seems necessary, crashes are reported calling
popViewControllerAnimated:
in this method. - The Back button remains grayed out, when pop is cancelled.
Swipe back gesture
We need to intercept the gesture by setting the delegate as is done in https://stackoverflow.com/a/23173035/2400328 .
If the UINavigationController
is subclassed, that would be:
self.interactivePopGestureRecognizer.delegate = self
and implementing:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
Take care in when you modify the delegate property, as it gets modified after the initializer is called.
Not calling popViewControllerAnimated:
Although undocumented, calling popViewControllerAnimated:
can be avoided as in https://stackoverflow.com/a/26084150/2400328.
It involves calling navigationBar:shouldPopItem:
of UINavigationController
(from the subclass).
The Back button
Although this may be a minor detail (especially, if you have designed your own Back button), there is a simple solution (written by me :) https://stackoverflow.com/a/29440633/2400328
You only need to set a property YES and NO.
auto item = navigationBar.topItem;
item.hidesBackButton = YES;
item.hidesBackButton = NO;
You can use a custom button with a graphics, which looks exactly like "Back" button and create a custom leftBarButtonItem view as UIButton
with this graphics. Add target self
to your button with custom back:
selector and pop your alert there. If the user presses "yes" to quit dismiss this view controller, if not - do nothing. The trick here is the button which looks exactly as navigation bar's back button.
Its better if u make your own back button and make it the left button of the Navigation controller. That can definitely help u to perform any action
If you're looking for a way to do this in Swift on iOS 10, you can create a custom UINavigationController
and then a UINavigationBarDelegate
extension:
class MyNavigationController : UINavigationController {
}
extension MyNavigationController : UINavigationBarDelegate {
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
return false
}
}
The Method
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
is doing what you want. Unfortunately we are not supposed to delegate UINavigationBar to our own objects :(
The Apple Documentation states :
... In addition, a navigation controller object automatically assigns itself as the delegate of its UINavigationBar object and prevents other objects from changing that relationship. ...
One/The? way to do what you want is to put in your own back-button. In that Method you do your tests and call
[self.navigationController popViewControllerAnimated:true];
if the user is allowed to go back.
精彩评论