How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem? How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem? ios ios

How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem?


First set delegate in viewDidLoad:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

And then disable gesture when pushing:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {    [super pushViewController:viewController animated:animated];    self.interactivePopGestureRecognizer.enabled = NO;}

And enable in viewDidDisappear:

self.navigationController.interactivePopGestureRecognizer.enabled = YES;

Also, add UINavigationControllerDelegate to your view controller.


You need to handle two scenarios:

  1. When you're pushing a new view onto the stack
  2. When you're showing the root view controller

If you just need a base class you can use, here's a Swift 3 version:

import UIKitfinal class SwipeNavigationController: UINavigationController {        // MARK: - Lifecycle        override init(rootViewController: UIViewController) {        super.init(rootViewController: rootViewController)         delegate = self    }        override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)                delegate = self    }    required init?(coder aDecoder: NSCoder) {         super.init(coder: aDecoder)         delegate = self     }        override func viewDidLoad() {        super.viewDidLoad()                // This needs to be in here, not in init        interactivePopGestureRecognizer?.delegate = self    }        deinit {        delegate = nil        interactivePopGestureRecognizer?.delegate = nil    }        // MARK: - Overrides        override func pushViewController(_ viewController: UIViewController, animated: Bool) {        duringPushAnimation = true                super.pushViewController(viewController, animated: animated)    }        // MARK: - Private Properties        fileprivate var duringPushAnimation = false}// MARK: - UINavigationControllerDelegateextension SwipeNavigationController: UINavigationControllerDelegate {        func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {        guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }                swipeNavigationController.duringPushAnimation = false    }    }// MARK: - UIGestureRecognizerDelegateextension SwipeNavigationController: UIGestureRecognizerDelegate {        func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {        guard gestureRecognizer == interactivePopGestureRecognizer else {            return true // default value        }                // Disable pop gesture in two situations:        // 1) when the pop animation is in progress        // 2) when user swipes quickly a couple of times and animations don't have time to be performed        return viewControllers.count > 1 && duringPushAnimation == false    }}

If you end up needing to act as a UINavigationControllerDelegate in another class, you can write a delegate forwarder similar to this answer.

Adapted from source in Objective-C: https://github.com/fastred/AHKNavigationController


It works for me when I set the delegate

self.navigationController.interactivePopGestureRecognizer.delegate = self;

and then implement

Swift

extension MyViewController:UIGestureRecognizerDelegate {    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {        return true    }}

Objective-C

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{    return YES;}