Due to a weird request which I tried to turn down but it didn't work, I had to override the navigationBar's back Button.
I have made a custom UINavigationController subclass and hacked the
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
method.
Here is my code:
@interface CustomUINavigationController ()
@end
@implementation CustomUINavigationController
#pragma mark - UINavigationBar delegate methods
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
if ([[self.viewControllers lastObject] isKindOfClass:[ViewController1 class]]) {
ViewController1 *vc1 = (ViewController1 *)[self.viewControllers lastObject];
[vc1 handleBackAction];
if (vc1.canPopVC == YES) {
[self popViewControllerAnimated:YES];
return YES;
} else {
return NO;
}
}
[self popViewControllerAnimated:YES];
return YES;
}
@end
All works fine, except when I pop a viewController programmatically. The app crashed every time when I wanted to perform a push after said pop. Turning NSZombie on
, revealed that when popping a viewController programmatically, its parent viewController is deallocated.
At this point, making a custom backButton is not a option since it will lose the native iOS 7 swipe to popViewController feature.
Crash log:
*** -[ContactsDetailViewController performSelector:withObject:withObject:]: message sent to deallocated instance 0x1806b790
I'm not 100% certain but I don't think you should actually be popping the view controller in that delegate method.
"should" delegate methods don't normally do something. They just assert whether something should or shouldn't be done.
Change your method to this...
And see if it works.
All I have done is removed the
popViewController
calls.EDIT - How to add a custom back button
In a category on
UIBarButtonItem
...Now whenever you want to set a custom back button just use...
I would suggest a completely different approach.
Create a base class for the view controllers that you are pushing on the navigation stack. In the
viewDidLoad
method set your custom button as theleftBarButtonItem
of thenavigationItem
and add a-backAction:
which invokes thepopViewControllerAnimated:
method of the navigation controller.That way you won't care about things like losing functionality of
UINavigationController
like the swipe to pop and you won't have to override thenavigationBar:shouldPopItem:
method at all.You probably need to do
[super shouldPop...
instead of actual[self popViewControllerAnimated:YES];
.The reason being that the way
UINavigationController
implements stack is private, so you should mess with the method calls as little as possible.Anyway, this looks like a hack. Moreover, the user will have no visual clue that you are blocking the navigation action. What's wrong with disabling the button via:
(My previous post was completely wrong. This is a complete rewrite with an appropriate solution.)
I had this behavior pop up when I chose to delete some code generating a warning when I was converting to ARC -- code that I thought was not being called.
Here's the situation:
If you shadow
navigationBar:shouldPopItem:
in a subclass of UINavigationController, then the current view controller will NOT be popped when the user touches the NavBar's BACK button. However, if you callpopViewControllerAnimated:
directly, yournavigationBar:shouldPopItem:
will still be called, and the view controller will pop.Here's why the view controller fails to pop when the user touches the BACK button:
UINavigationController has a hidden method called
navigationBar:shouldPopItem:
. This method IS called when the user clicks the BACK button, and it is the method that normally callspopViewControllerAnimated:
when the user touches the BACK button.When you shadow
navigationBar:shouldPopItem:
, the super class' implementation is not called, and hence the ViewController is not popped.Why you should NOT call
popViewControllerAnimated:
within your subclass'navigationBar:shouldPopItem:
:If you call
popViewControllerAnimated:
withinnavigationBar:shouldPopItem:
, you will see the behavior that you desire when you click the BACK button on the NavBar: You can determine whether or not you want to pop, and your view controller pops if you want it to.But, if you call
popViewControllerAnimated:
directly, you will end up popping two view controllers: One from your direct call topopViewControllerAnimated:
, and one from the call you added to withinnavigationBar:shouldPopItem:
.What I believe to be the safe solution:
Your custom nav controller should be declared like this:
Your implementation should contain code that looks something like this: