(Updated) Swift 4.2
I found that other published solutions overriding the delegate or setting it to zero caused unexpected behavior.
In my case, when I was at the top of the navigation stack and tried to use a gesture to push another one, it would fail (as expected), but subsequent attempts to push it onto the stack would start to cause strange graphical crashes in the Navigation Bar. This makes sense because the delegate is used not only to block gesture recognition when the navigation bar is hidden, but also to throw away all this other behavior.
From my testing, gestureRecognizer(_:, shouldReceiveTouch:) that gestureRecognizer(_:, shouldReceiveTouch:) is a method that implements the original delegate to block gesture recognition when the navigation bar is hidden, and not gestureRecognizerShouldBegin(_:) . Other solutions that implement gestureRecognizerShouldBegin(_:) in their gestureRecognizerShouldBegin(_:) work because the lack of implementation of gestureRecognizer(_:, shouldReceiveTouch:) will cause the default behavior of getting all touches.
The @Nathan Perry solution is approaching, but without the responsedsToSelector respondsToSelector(_:) implementation, the UIKit code that sends messages to the delegate will assume that there is no implementation for any of the other delegate methods, and forwardingTargetForSelector(_:) will never get called.
So, we take control of 'gestRecognizer (_:, shouldReceiveTouch :) in one specific scenario in which we want to change the behavior, and otherwise redirect everything else to the delegate.
import Foundation class AlwaysPoppableNavigationController: UINavigationController { private let alwaysPoppableDelegate = AlwaysPoppableDelegate() override func viewDidLoad() { super.viewDidLoad() alwaysPoppableDelegate.originalDelegate = interactivePopGestureRecognizer?.delegate alwaysPoppableDelegate.navigationController = self interactivePopGestureRecognizer?.delegate = alwaysPoppableDelegate } } final class AlwaysPoppableDelegate: NSObject, UIGestureRecognizerDelegate { weak var navigationController: UINavigationController? weak var originalDelegate: UIGestureRecognizerDelegate? override func responds(to aSelector: Selector!) -> Bool { if aSelector == #selector(gestureRecognizer(_:shouldReceive:)) { return true } else if let responds = originalDelegate?.responds(to: aSelector) { return responds } else { return false } } override func forwardingTarget(for aSelector: Selector!) -> Any? { return originalDelegate } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if let nav = navigationController, nav.isNavigationBarHidden, nav.viewControllers.count > 1 { return true } else if let result = originalDelegate?.gestureRecognizer?(gestureRecognizer, shouldReceive: touch) { return result } else { return false } } }
Chris Vasselli Jun 26 '16 at 20:26 2016-06-26 20:26
source share