Mis à jour pour iOS 13.4
iOS 13.4 a cassé la solution précédente, donc les choses vont devenir moche. Il semble que dans iOS 13.4, ce comportement est désormais contrôlé par une méthode privée _gestureRecognizer:shouldReceiveEvent:
(à ne pas confondre avec la nouvelle shouldReceive
méthode publique ajoutée dans iOS 13.4).
J'ai trouvé que d'autres solutions publiées remplaçant le délégué ou le définissant sur nul provoquaient un comportement inattendu.
Dans mon cas, lorsque j'étais au sommet de la pile de navigation et que j'essayais d'utiliser le geste pour en faire apparaître un de plus, cela échouait (comme prévu), mais les tentatives ultérieures de pousser sur la pile commenceraient à provoquer d'étranges problèmes graphiques dans le barre de navigation. Cela a du sens, car le délégué est utilisé pour gérer plus que simplement s'il faut ou non empêcher le geste d'être reconnu lorsque la barre de navigation est masquée et que tous ces autres comportements ont été rejetés.
D'après mes tests, il semble que ce gestureRecognizer(_:, shouldReceiveTouch:)
soit la méthode que le délégué d'origine met en œuvre pour empêcher le geste d'être reconnu lorsque la barre de navigation est masquée, non gestureRecognizerShouldBegin(_:)
. D'autres solutions qui implémentent gestureRecognizerShouldBegin(_:)
dans leur travail de délégué parce que l'absence d'une implémentation de gestureRecognizer(_:, shouldReceiveTouch:)
provoquera le comportement par défaut de recevoir toutes les touches.
La solution de @Nathan Perry se rapproche, mais sans implémentation de respondsToSelector(_:)
, le code UIKit qui envoie des messages au délégué croira qu'il n'y a pas d'implémentation pour aucune des autres méthodes de délégué, et forwardingTargetForSelector(_:)
ne sera jamais appelé.
Donc, nous prenons le contrôle de `gestureRecognizer (_ :, shouldReceiveTouch :) dans le scénario spécifique, nous voulons modifier le comportement, et sinon transmettre tout le reste au délégué.
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
}
}
}