[ios] How to disable back swipe gesture in UINavigationController on iOS 7

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?

The answer is


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;
}

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


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.


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.


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;
}

swift 5, swift 4.2 can use the code in the below.

// disable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// enable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true

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.


For Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false

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

I've refined Twan's answer a bit, because:

  1. your view controller may be set as a delegate to other gesture recognisers
  2. 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;
}

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


This is the way on Swift 3

works for me

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

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
    }

}

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;
}

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
}

Just remove gesture recognizer from NavigationController. Work in iOS 8.

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    [self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];

Examples related to ios

Adding a UISegmentedControl to UITableView Crop image to specified size and picture location Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Autoresize View When SubViews are Added Warp \ bend effect on a UIView? Speech input for visually impaired users without the need to tap the screen make UITableViewCell selectable only while editing Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64

Examples related to objective-c

Adding a UISegmentedControl to UITableView Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Warp \ bend effect on a UIView? Use NSInteger as array index Detect if the device is iPhone X Linker Command failed with exit code 1 (use -v to see invocation), Xcode 8, Swift 3 ITSAppUsesNonExemptEncryption export compliance while internal testing? How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem? Change status bar text color to light in iOS 9 with Objective-C

Examples related to uinavigationcontroller

How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem? How to hide a navigation bar from first ViewController in Swift? Execute action when back bar button of UINavigationController is pressed Programmatically navigate to another view controller/scene How to force view controller orientation in iOS 8? presenting ViewController with NavigationViewController swift How to Navigate from one View Controller to another using Swift UINavigationBar Hide back Button Text How to check if a view controller is presented modally or pushed on a navigation stack? Trying to handle "back" navigation button action in iOS

Examples related to uigesturerecognizer

How to call gesture tap on UIView programmatically in swift tap gesture recognizer - which object was tapped? How to disable back swipe gesture in UINavigationController on iOS 7 UITapGestureRecognizer - single tap and double tap UIGestureRecognizer on UIImageView

Examples related to ios7

Xcode: Could not locate device support files How to Apply Gradient to background view of iOS Swift App How do I hide the status bar in a Swift iOS app? Using Predicate in Swift How do I programmatically set device orientation in iOS 7? What is the height of Navigation Bar in iOS 7? Warning :-Presenting view controllers on detached view controllers is discouraged Color Tint UIButton Image iOS change navigation bar title font and color How to embed small icon in UILabel