iOS13.4用に更新
iOS 13.4は以前の解決策を破ったので、物事は醜くなるでしょう。iOS 13.4では、この動作はプライベートメソッドによって制御さ_gestureRecognizer:shouldReceiveEvent:
れるようになりshouldReceive
ました(iOS 13.4で追加された新しいパブリックメソッドと混同しないでください)。
デリゲートをオーバーライドしたり、nilに設定したりすると、他の投稿されたソリューションが予期しない動作を引き起こすことがわかりました。
私の場合、ナビゲーションスタックの一番上にいて、ジェスチャを使用してもう一度ポップしようとすると、(予想どおり)失敗しましたが、スタックにプッシュしようとすると、奇妙なグラフィックの不具合が発生し始めました。ナビゲーションバー。これは理にかなっています。なぜなら、デリゲートは、ナビゲーションバーが非表示になっているときにジェスチャが認識されないようにブロックするかどうかだけでなく、他のすべての動作が破棄されているためです。
私のテストから、gestureRecognizer(_:, shouldReceiveTouch:)
ナビゲーションバーが非表示になっているときにジェスチャが認識されないようにするために、元のデリゲートが実装している方法のようgestureRecognizerShouldBegin(_:)
です。の実装gestureRecognizerShouldBegin(_:)
がないためにデリゲートで実装される他のソリューションgestureRecognizer(_:, shouldReceiveTouch:)
は、すべてのタッチを受信するデフォルトの動作を引き起こします。
@Nathan Perryのソリューションは近づいていますが、の実装がないrespondsToSelector(_:)
と、デリゲートにメッセージを送信するUIKitコードは、他のデリゲートメソッドの実装がないと信じて、forwardingTargetForSelector(_:)
呼び出されることはありません。
したがって、動作を変更する1つの特定のシナリオでは、 `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
}
}
}