How to disable back swipe gesture in UINavigationController on iOS 7

160,408

Solution 1

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

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

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

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

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;
}
Share:
160,408
ArtFeel
Author by

ArtFeel

iOS\tvOS Developer

Updated on July 08, 2022

Comments

  • ArtFeel
    ArtFeel almost 2 years

    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?

  • Ant
    Ant over 10 years
    This causes an invalid argument exception when run on iOS6.
  • ArtFeel
    ArtFeel over 10 years
    Of course, you need to check the availability of new methods if you are supporting old versions of the iOS.
  • Marc
    Marc over 10 years
    Is there a way to disable it for a potion of the view?
  • ArtFeel
    ArtFeel over 10 years
    You can enable / disable recognizer on viewDidAppear: / viewDidDisappear. Or, you can implement UIGestureRecognizerDelegate protocol with your more complex logic and set it as recognizer.delegate property.
  • Eric Chen
    Eric Chen over 9 years
    Thanks! This is required to fully disable back swipe. It still exists in iOS 8, and smells like an Apple bug.
  • Myxtic
    Myxtic over 9 years
    I can confirm that this still works for me on iOS 8.
  • Sihad Begovic
    Sihad Begovic over 9 years
    On iOS8, setting self.navigationController.interactivePopGestureRecognizer.en‌​abled property does not work in following view's methods: viewDidLoad, viewWillAppear, viewDidAppear, viewDidDisappear, but works in method viewWillDisappear. On iOS7 it works in all of above mentioned methods. So try to use it in any other methods while working on the viewController, I confirm it works for me on iOS8 when I click on some button inside of the view.
  • Charlie Seligman
    Charlie Seligman over 9 years
    Those having issues with iOS 8 have a look at my updated answer below. This worked for me - hopefully it will work for you too.
  • Rambatino
    Rambatino over 9 years
    Yer stick it in ViewWillLayoutSubviews - although I guess it will get called multiple times...
  • David Douglas
    David Douglas over 9 years
    While this works with iOS8 I get a warning on the line *.delegate = self; stating: Assigning to id<UIGestureRecognizerDelegate>' from incompatible type 'ViewController *const __strong'
  • Admin
    Admin over 9 years
    When promoting a project you're involved with you must disclose your affiliation with it.
  • Admin
    Admin over 9 years
    Moreover, your project's only purpose is to manually enable the swipe gesture when the default system one isn't working, whereas the question asks how to disable that system wide gesture, so even if you set self.navigationController.swipeBackEnabled = NO I'm pretty sure this will only disable your library's swipe back gesture but the system's one will still be enabled.
  • Alexandre G
    Alexandre G about 9 years
    As of iOS8, the accepted answer still works as expected. You are probably doing something else wrong..
  • tonytastic
    tonytastic about 9 years
    Can confirm that this will not work in iOS8 in viewDidLoad and viewWillAppear, putting it into viewwilllayoutgubviews did the trick
  • Charlie Seligman
    Charlie Seligman about 9 years
    Managed to get it semi working by calling the accepted answer in viewWillLayoutSubviews. However, swiping did cause the page to call 'viewDidLoad' again so reverted back to my answer above
  • devxoul
    devxoul about 9 years
    Sorry for my short answer, I've just edited my answer with additional information: "useful for specific navigation controllers". Thanks!
  • lifjoy
    lifjoy about 9 years
    @DavidDouglas: perhaps you could eliminate the warning with this code: __weak __typeof(self) theSafeSelf = self? Then set the delegate to theSafeSelf.
  • Ahsan Ebrahim
    Ahsan Ebrahim about 9 years
    I dont know why but a view controller in my app for some unknown reason was crashing on this back gesture.. this saved me from finding it as I didnt needed this back gesture and so i disabled using this code.. +1
  • Matt
    Matt almost 9 years
    It appears to use swizzle which is no longer allowed. iOS8?
  • devxoul
    devxoul almost 9 years
    @Matt, Sorry, I couldn't understand what you mean. Could you please explain me "~ no longer allowed. iOS 8?" means? Thanks!
  • devxoul
    devxoul almost 9 years
    @André, SwipeBack plays with native(system) gesture recognizer. You can manage native swipeback gesture enabled with it :)
  • Matt
    Matt almost 9 years
    @devxoul I am sorry! I thought I had read something a while ago saying that swizzling was no longer allowed. However, I cannot find anything that says this. Guess I am wrong.
  • phatmann
    phatmann almost 9 years
    @AhsanEbrahim, when the back gesture starts, viewWillAppear is called on the view behind the current view. This can cause havoc in code logic since the current view is still active. Might be the cause of your crash.
  • SFF
    SFF over 8 years
    If it's for DetailViewController of UISplitViewController like mine, use self.navigationController.navigationController.interactivePo‌​pGestureRecognizer.e‌​nabled = NO;
  • Seth Caldwell
    Seth Caldwell over 8 years
    Anyone know if there's a plugin for phonegap to accomplish this?
  • kalperin
    kalperin over 8 years
    Unless you know when the gesture recognizer is installed on the view, waiting an arbitrary amount of time to disable it may or may not work.
  • Dannie P
    Dannie P over 8 years
    @kalperin it's not guaranteed to work, though it's a very handy solution at some times. Use your own reasoning.
  • Chirag Lukhi
    Chirag Lukhi about 8 years
    It working for me having version greater than iOS 8.1 :)
  • João Nunes
    João Nunes about 8 years
    I used this lib and it comes with the same some bugs. The trick is to do like FB is doing!
  • Umair
    Umair about 8 years
    I also found out that if you set navigationItem.hidesBackButton = true, this gesture also gets disabled. In my case i implemented a custom back button and add as a leftBarButtonItem
  • HixField
    HixField almost 8 years
    This does not work in iOS9. The below answer from Antoine (also setting the delegate) does work. Its a bit confusing, because if you put it in e.g. viewDidAppear it seams to work sometimes, but this actually happens because the first time you edge-swipe it brings up the underlying vc, and then calls the viewDidAppear again after which edge-swipe is indeed disabled. However its not disabled when the screen is initially presented!
  • primehalo
    primehalo over 7 years
    @DavidDouglas: You need to add <UIGestureRecognizerDelegate> to the interface to get rid of that warning
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    Are the enabled yes/no lines needed? You return NO from gestureRecognizerShouldBegin, isn't that sufficient?
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    As per Jack's answer, may be safer (correct in more situations), instead of clearing with nil in viewWillDisappear, to save the previous value in viewDidAppear,then restore to that saved value.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    viewDidLoad plus delay is a risky programming practice. A bad habit to start. What if user starts the swipe before your delayed call kicks in? There is no safe time that is guaranteed to be long enough yet not too long. That is why other answers, posted long before yours, suggest placing the code in viewDidAppear. That ensures that everything is installed. Don't invent arbitrary delays; use Apple's sequence of calls as intended.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    I am trying to imagine under what circumstances this would work, but Jack's would not. You say you tried all of the other answers: what went wrong when you tried Jack's?
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    On the other hand, this does seem simpler than Jack's, so maybe its not important. Decided I like this, because don't have to declare my class as a delegate, nor manipulate interactivePopGestureRecognizer.delegate.
  • ToolmakerSteve
    ToolmakerSteve over 7 years
    BTW, code can be simplified. Remove if( .. respondsToSelector ... The next line sets popGesture to a recognizer, or to nil. Then use its value: if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture ).
  • Dannie P
    Dannie P over 7 years
    @iChirag true. I've noted that for 8.1 you need 0.5 second delay
  • Dannie P
    Dannie P over 7 years
    @ToolmakerSteve the whole point of using this delay is that it does not work via iOS 8 in callbacks like viewDidAppear, which looks like a UIKit bug. This gif demonstrates the issue. Sure one should use viewWillAppear when it works. When one sees a delay like this one knows it's potentially unsafe, so own reasoning should be applied. Nevertheless it is a nice and quick workaround on iOS 8.
  • Dannie P
    Dannie P over 7 years
    UIKit engineers specified in docs this You can use this property to retrieve the gesture recognizer and tie it to the behavior of other gesture recognizers in your user interface. This tells nothing about disabling this gesture, so technically it's not a bug, rather not yet implemented feature request.
  • ooops
    ooops over 7 years
    Also works in iOS 10, this should be the accepted answer. By the way, if you want to re-enable it, do [self.navigationController.view addGestureRecognizer:self.navigationController.interactivePo‌​pGestureRecognizer] somewhere.
  • Womble
    Womble about 7 years
    This works, though I'd suggest using optional chaining instead of force unwrapping. e.g. self.navigationController?.interactivePopGestureRecognizer?.‌​isEnabled = false
  • Josh Bernfeld
    Josh Bernfeld about 6 years
    You should not override the interactive pop gesture delegate as it will cause undocumented behavior
  • Matt
    Matt about 6 years
    For anyone who was stuck on this like I was, if you are doing this for a master-detail view in a split view controller, you have to do the same for self.navigationController.navigationController. See stackoverflow.com/a/50012503/5605365
  • Alyoshak
    Alyoshak about 6 years
    The second line of code in the if statement, the one that assigns self as the delegate, generates a build error for me: Assigning to 'id<UIGestureRecognizerDelegate> _Nullable' from incompatible type 'VCPrivateData *const __strong'. But commenting that line out and leaving the first line disabled things fine. I did not need to implement gestureRecognizerShouldBegin either.
  • Lokesh SN
    Lokesh SN over 5 years
    I think it's not really overriding the delegate but just modifying the Boolean variable that they've provided for this very purpose, so it won't be a problem
  • albertamg
    albertamg almost 5 years
    +1 "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."
  • albertamg
    albertamg almost 5 years
    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.
  • albertamg
    albertamg almost 5 years
    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.