Оновлено для iOS 13.4
iOS 13.4 зламав попереднє рішення, тому все стане неприємним. Схоже, в iOS 13.4 ця поведінка тепер контролюється приватним методом _gestureRecognizer:shouldReceiveEvent:
(не плутати з новим загальнодоступним shouldReceive
методом, доданим в iOS 13.4).
Я виявив, що інші опубліковані рішення, які перевизначають делегата, або встановлення його на нуль спричинили несподівану поведінку.
У моєму випадку, коли я знаходився на вершині стека навігації і намагався використати жест, щоб вивести ще один, він зазнав би невдачі (як і передбачалося), але подальші спроби натиснення на стек почали б викликати дивні графічні збої в панель навігації. Це має сенс, оскільки делегат використовується не лише для того, щоб заблокувати розпізнавання жесту, коли навігаційна панель прихована, і вся ця інша поведінка була викинута.
З мого тестування виявляється, що 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
}
}
}