Обновлено для iOS 13.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(_:)
никогда не будет вызван.
Итак, мы берем на себя управление 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
}
}
}