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

前端 未结 14 858
遥遥无期
遥遥无期 2020-11-28 22:48

I got the opposite issue from here. By default in iOS7, back swipe gesture of UINavigationController\'s stack could pop the presented ViewCon

相关标签:
14条回答
  • 2020-11-28 23:18

    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;
    }
    
    0 讨论(0)
  • 2020-11-28 23:21

    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.

    0 讨论(0)
  • 2020-11-28 23:24

    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 UIKit
    
    final class SwipeNavigationController: UINavigationController {
    
        // MARK: - Lifecycle
    
        override init(rootViewController: UIViewController) {
            super.init(rootViewController: rootViewController)
        }
    
        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: - UINavigationControllerDelegate
    
    extension SwipeNavigationController: UINavigationControllerDelegate {
    
        func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
            guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }
    
            swipeNavigationController.duringPushAnimation = false
        }
    
    }
    
    // MARK: - UIGestureRecognizerDelegate
    
    extension 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

    0 讨论(0)
  • 2020-11-28 23:26

    This answer, but with storyboard support.

    class SwipeNavigationController: UINavigationController {
    
        // MARK: - Lifecycle
    
        override init(rootViewController: UIViewController) {
            super.init(rootViewController: rootViewController)
        }
    
        override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
            super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    
            self.setup()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
    
            self.setup()
        }
    
        private func setup() {
            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
    }
    
    0 讨论(0)
  • 2020-11-28 23:28

    In Swift you can do the following code

    import UIKit
    extension UINavigationController: UIGestureRecognizerDelegate {
    
        open override func viewDidLoad() {
            super.viewDidLoad()
            interactivePopGestureRecognizer?.delegate = self
        }
    
        public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
            return viewControllers.count > 1
        }
    }
    

    Above code helps in swift left to go back to previous controller like Facebook, Twitter.

    0 讨论(0)
  • 2020-11-28 23:29

    Most answers are pertaining to doing it on code. But I'll give you one that works on Storyboard. Yes! You read it right.

    • Click on main UINavigationController and navigate to it's Identity Inspector tab.

    • Under User Defined Runtime Attributes, set a single runtime property called interactivePopGestureRecognizer.enabled to true. Or graphically, you'd have to enable the checkbox as shown in the image below.

    That's it. You're good to go. Your back gesture will work as if it was there all along.

    Image displaying the property that out to be set

    0 讨论(0)
提交回复
热议问题