How to disable back swipe gesture in UINavigationController on iOS 7
IosObjective CUinavigationcontrollerUigesturerecognizerIos7Ios Problem Overview
In iOS 7 Apple added a new default navigation behavior. You can swipe from the left edge of the screen to go back on the navigation stack. But in my app, this behavior conflicts with my custom left menu. So, is it possible to disable this new gesture in UINavigationController?
Ios Solutions
Solution 1 - Ios
I found a solution:
Objective-C:
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
Swift 3+:
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
Solution 2 - Ios
I found out setting the gesture to disabled only doesn't always work. It does work, but for me it only did after I once used the backgesture. Second time it wouldn't trigger the backgesture.
Fix for me was to delegate the gesture and implement the shouldbegin method to return NO:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Disable iOS 7 back gesture
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// Enable iOS 7 back gesture
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return NO;
}
Solution 3 - Ios
Just remove gesture recognizer from NavigationController. Work in iOS 8.
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
[self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];
Solution 4 - Ios
As of iOS 8 the accepted answer no longer works. I needed to stop the swipping to dismiss gesture on my main game screen so implemented this:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.delegate = nil;
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return NO;
}
Solution 5 - Ios
I've refined Twan's answer a bit, because:
- your view controller may be set as a delegate to other gesture recognisers
- setting the delegate to
nil
leads to hanging issues when you go back to the root view controller and make a swipe gesture before navigating elsewhere.
The following example assumes iOS 7:
{
id savedGestureRecognizerDelegate;
}
- (void)viewWillAppear:(BOOL)animated
{
savedGestureRecognizerDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
- (void)viewWillDisappear:(BOOL)animated
{
self.navigationController.interactivePopGestureRecognizer.delegate = savedGestureRecognizerDelegate;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
return NO;
}
// add whatever logic you would otherwise have
return YES;
}
Solution 6 - Ios
For Swift:
navigationController!.interactivePopGestureRecognizer!.enabled = false
Solution 7 - Ios
Please set this in root vc:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:YES];
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:YES];
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
Solution 8 - Ios
swift 5, swift 4.2 can use the code in the below.
// disable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// enable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
Solution 9 - Ios
EDIT
If you want to manage swipe back feature for specific navigation controllers, consider using SwipeBack.
With this, you can set navigationController.swipeBackEnabled = NO
.
For example:
#import <SwipeBack/SwipeBack.h>
- (void)viewWillAppear:(BOOL)animated
{
navigationController.swipeBackEnabled = NO;
}
It can be installed via CocoaPods.
pod 'SwipeBack', '~> 1.0'
I appologize for lack of explanation.
Solution 10 - Ios
it works for me in ios 10 and later :
- (void)viewWillAppear:(BOOL)animated {
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
}
it doesnt work on viewDidLoad() method.
Solution 11 - Ios
My method. One gesture recognizer to rule them all:
class DisabledGestureViewController: UIViewController: UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController!.interactivePopGestureRecognizer!.delegate = self
}
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
// Prevent going back to the previous view
return !(navigationController!.topViewController is DisabledGestureViewController)
}
}
Important: don't reset the delegate anywhere in the navigation stack: navigationController!.interactivePopGestureRecognizer!.delegate = nil
Solution 12 - Ios
This is the way on Swift 3
works for me
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
Solution 13 - Ios
All of these solutions manipulate Apple's gesture recognizer in a way they do not recommend. I've just been told by a friend that there's a better solution:
[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];
where myPanGestureRecognizer is the gesture recognizer you are using to e.g. show your menu. That way, Apple's gesture recognizer doesn't get turned back on by them when you push a new navigation controller and you don't need to rely on hacky delays that may fire too early if your phone is put to sleep or under heavy load.
Leaving this here because I know I'll not remember this the next time I need it, and then I'll have the solution to the issue here.
Solution 14 - Ios
This works in viewDidLoad:
for iOS 8:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.navigationController.interactivePopGestureRecognizer.enabled = false;
});
Lots of the problems could be solved with help of the good ol' dispatch_after
.
Though please note that this solution is potentially unsafe, please use your own reasoning.
Update
For iOS 8.1 delay time should be 0.5 seconds
On iOS 9.3 no delay needed anymore, it works just by placing this in your viewDidLoad
:
(TBD if works on iOS 9.0-9.3)
navigationController?.interactivePopGestureRecognizer?.enabled = false
Solution 15 - Ios
None of the given answers helped me to resolve the issue. Posting my answer here; may be helpful for someone
Declare private var popGesture: UIGestureRecognizer?
as global variable in your viewcontroller. Then implement the code in viewDidAppear and viewWillDisappear methods
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if self.navigationController!.respondsToSelector(Selector("interactivePopGestureRecognizer")) {
self.popGesture = navigationController!.interactivePopGestureRecognizer
self.navigationController!.view.removeGestureRecognizer(navigationController!.interactivePopGestureRecognizer!)
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.popGesture != nil {
navigationController!.view.addGestureRecognizer(self.popGesture!)
}
}
This will disable swipe back in iOS v8.x onwards
Solution 16 - Ios
It worked for me for most of the viewcontrollers.
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
It wasn't not working for some viewcontrollers like UIPageViewController. On UIPageViewController's pagecontentviewcontroller below code worked for me.
override func viewDidLoad() {
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
}
On UIGestureRecognizerDelegate,
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
return false
}
return true
}
Solution 17 - Ios
For Swift 4 this works:
class MyViewController: UIViewController, UIGestureRecognizerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.interactivePopGestureRecognizer?.gesture.delegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
self.navigationController?.interactivePopGestureRecognizer?.gesture.isEnabled = false
}
}
Solution 18 - Ios
self.navigationController.pushViewController(VC, animated: Bool)
call
self.navigationController.setViewContollers([VC], animated: Bool)
setViewControllers replaces all the VCs on the stack, instead of adding a new controller on top. This means that the new set VC is the root VC, and the user cannot go back.
This is most effective when you only want to disable the swipe on a single VC and keep the swipe-to-back for the other VC.
If you want users to be able to go back, just not through swiping, do not use this method as it will disable all backs (as there is no VC to go back to)