更新于iOS 13.4
iOS 13.4打破了以前的解决方案,因此事情变得很丑陋。看起来在iOS 13.4中,此行为现在由私有方法控制_gestureRecognizer:shouldReceiveEvent:
(不要与shouldReceive
iOS 13.4中添加的新public方法混淆)。
我发现其他发布的解决方案会覆盖该委托,或者将其设置为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
}
@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
}
}
@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, *) {
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
}
}
}