Changing back button in iOS 7 disables swipe to navigate back
I have an iOS 7 app where I am setting a custom back button like this:
UIImage *backButtonImage = [UIImage imageNamed:@"back-button"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:backButtonImage forState:UIControlStateNormal];
backButton.frame = CGRectMake(0, 0, 20, 20);
[backButton addTarget:self
action:@selector(popViewController)
forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
viewController.navigationItem.leftBarButtonItem = backBarButtonItem;
But this disables the iOS 7 "swipe left to right" gesture to navigate to the previous controller. Does anyone know how I can set a custom button and still keep this gesture enabled?
EDIT: I tried to set the viewController.navigationItem.backBarButtonItem instead, but this doesn't seem to show my custom image.
IMPORTANT: This is a hack. I would recommend taking a look at this answer.
Calling the following line after assigning the leftBarButtonItem
worked for me:
self.navigationController.interactivePopGestureRecognizer.delegate = self;
Edit:
This does not work if called in init
methods. It should be called in viewDidLoad
or similar methods.
Use the backIndicatorImage and backIndicatorTransitionMaskImage properties of the UINavigationBar if at all possible. Setting these on an a UIAppearanceProxy can easily modify behavior across your application. The wrinkle is that you can only set those on ios 7, but that works out because you can only use the pop gesture on ios 7 anyway. Your normal ios 6 styling can remain intact.
UINavigationBar* appearanceNavigationBar = [UINavigationBar appearance];
//the appearanceProxy returns NO, so ask the class directly
if ([[UINavigationBar class] instancesRespondToSelector:@selector(setBackIndicatorImage:)])
{
appearanceNavigationBar.backIndicatorImage = [UIImage imageNamed:@"back"];
appearanceNavigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"back"];
//sets back button color
appearanceNavigationBar.tintColor = [UIColor whiteColor];
}else{
//do ios 6 customization
}
Trying to manipulate the interactivePopGestureRecognizer's delegate will lead to a lot of issues.
I saw this solution http://keighl.com/post/ios7-interactive-pop-gesture-custom-back-button/ which subclasses UINavigationController. Its a better solution as it handles the case where you swipe before the controller is in place - which causes a crash.
In addition to this I noticed if you do a swipe on the root view controller (after pushing on one, and back again) the UI becomes unresponsive (also same problem in answer above).
So the code in the subclassed UINavigationController should look like so:
@implementation NavigationController
- (void)viewDidLoad {
[super viewDidLoad];
__weak NavigationController *weakSelf = self;
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.delegate = weakSelf;
self.delegate = weakSelf;
}
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
// Hijack the push method to disable the gesture
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
}
[super pushViewController:viewController animated:animated];
}
#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animate {
// Enable the gesture again once the new controller is shown
self.interactivePopGestureRecognizer.enabled = ([self respondsToSelector:@selector(interactivePopGestureRecognizer)] && [self.viewControllers count] > 1);
}
@end