Search code examples
iosswiftuinavigationcontrollerios13xcode11

Swipe to popViewController not working in iOS 13


Below code, I have done for swipe to popViewController. and that's working for ios 12 or older version but when it's run in iOS 13 it is not working. the current version of Xcode is 11.1

Also, I am using a native NavigationController and navigation bar. So I don't think its an issue because of the Navigation controller.

import UIKit

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
}

// 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
      if (self.visibleViewController?.isKind(of: CheckOTPViewController.self))! {
        return false
      }
      if (self.visibleViewController?.isKind(of: ForgotPasswordViewController.self))! {
        return false
      }

        return viewControllers.count > 1 && duringPushAnimation == false
    }
}

Solution

  • I have fixed this issue finally after lots of try-catch. The issue is calling deinit method and not setting proper delegate on viewDidLoad Method.

    Now below is the working code.

    import UIKit
    
    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
            interactivePopGestureRecognizer?.delegate = self
            navigationItem.backBarButtonItem?.isEnabled = true
            interactivePopGestureRecognizer?.isEnabled = true
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // This needs to be in here, not in init
            setup()
        }
    
        // 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
          if (self.visibleViewController?.isKind(of: CheckOTPViewController.self))! {
            return false
          }
          if (self.visibleViewController?.isKind(of: ForgotPasswordViewController.self))! {
            return false
          }
    
            return viewControllers.count > 1 && duringPushAnimation == false
        }
    }