为 iOS 13.4 更新
iOS 13.4 打破了之前的解决方案,所以事情会变得很糟糕。看起来在 iOS 13.4 中,此行为现在由私有方法 _gestureRecognizer:shouldReceiveEvent: 控制(不要与 iOS 13.4 中添加的新公共 shouldReceive 方法混淆)。
我发现其他发布的解决方案覆盖委托或将其设置为 nil 会导致一些意外行为。
在我的情况下,当我在导航堆栈的顶部并尝试使用手势再弹出一个时,它会失败(如预期的那样),但随后尝试压入堆栈会开始导致奇怪的图形导航栏中的故障。这是有道理的,因为委托不仅仅用于处理在导航栏隐藏时是否阻止手势被识别,而且所有其他行为都被丢弃。
从我的测试来看,gestureRecognizer(_:, shouldReceiveTouch:) 似乎是原始委托实施的方法,用于在隐藏导航栏时阻止手势被识别,而不是gestureRecognizerShouldBegin(_:)。在其委托工作中实现 gestureRecognizerShouldBegin(_:) 的其他解决方案,因为缺少 gestureRecognizer(_:, shouldReceiveTouch:) 的实现将导致接收所有触摸的默认行为。
@Nathan Perry 的解决方案接近了,但如果没有实现 respondsToSelector(_:),向委托发送消息的 UIKit 代码将认为没有任何其他委托方法的实现,并且永远不会调用 forwardingTargetForSelector(_:) .
因此,在我们想要修改行为的一个特定场景中,我们控制 `gestureRecognizer(_:, shouldReceiveTouch:),否则将其他所有内容转发给委托。
class AlwaysPoppableNavigationController : UINavigationController {
private var alwaysPoppableDelegate: AlwaysPoppableDelegate!
override func viewDidLoad() {
super.viewDidLoad()
self.alwaysPoppableDelegate = AlwaysPoppableDelegate(navigationController: self, originalDelegate: self.interactivePopGestureRecognizer!.delegate!)
self.interactivePopGestureRecognizer!.delegate = self.alwaysPoppableDelegate
}
}
private class AlwaysPoppableDelegate : NSObject, UIGestureRecognizerDelegate {
weak var navigationController: AlwaysPoppableNavigationController?
weak var originalDelegate: UIGestureRecognizerDelegate?
init(navigationController: AlwaysPoppableNavigationController, originalDelegate: UIGestureRecognizerDelegate) {
self.navigationController = navigationController
self.originalDelegate = originalDelegate
}
// For handling iOS before 13.4
@objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if let navigationController = navigationController, navigationController.isNavigationBarHidden && navigationController.viewControllers.count > 1 {
return true
}
else if let originalDelegate = originalDelegate {
return originalDelegate.gestureRecognizer!(gestureRecognizer, shouldReceive: touch)
}
else {
return false
}
}
// For handling iOS 13.4+
@objc func _gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceiveEvent event: UIEvent) -> Bool {
if let navigationController = navigationController, navigationController.isNavigationBarHidden && navigationController.viewControllers.count > 1 {
return true
}
else if let originalDelegate = originalDelegate {
let selector = #selector(_gestureRecognizer(_:shouldReceiveEvent:))
if originalDelegate.responds(to: selector) {
let result = originalDelegate.perform(selector, with: gestureRecognizer, with: event)
return result != nil
}
}
return false
}
override func responds(to aSelector: Selector) -> Bool {
if #available(iOS 13.4, *) {
// iOS 13.4+ does not need to override responds(to:) behavior, it only uses forwardingTarget
return originalDelegate?.responds(to: aSelector) ?? false
}
else {
if aSelector == #selector(gestureRecognizer(_:shouldReceive:)) {
return true
}
else {
return originalDelegate?.responds(to: aSelector) ?? false
}
}
}
override func forwardingTarget(for aSelector: Selector) -> Any? {
if #available(iOS 13.4, *), aSelector == #selector(_gestureRecognizer(_:shouldReceiveEvent:)) {
return nil
}
else {
return self.originalDelegate
}
}
}