Aggiornato per iOS 13.4
iOS 13.4 ha rotto la soluzione precedente, quindi le cose si metteranno male. Sembra che in iOS 13.4 questo comportamento sia ora controllato da un metodo privato _gestureRecognizer:shouldReceiveEvent:
(da non confondere con il nuovo shouldReceive
metodo pubblico aggiunto in iOS 13.4).
Ho scoperto che altre soluzioni pubblicate che sovrascrivono il delegato o impostarlo su zero hanno causato un comportamento imprevisto.
Nel mio caso, quando ero in cima allo stack di navigazione e ho provato a utilizzare il gesto per visualizzarne un altro, non sarebbe riuscito (come previsto), ma i successivi tentativi di inserire nello stack avrebbero iniziato a causare strani problemi grafici nel barra di navigazione. Questo ha senso, perché il delegato viene utilizzato per gestire qualcosa di più che bloccare o meno il riconoscimento del gesto quando la barra di navigazione è nascosta e tutto l'altro comportamento è stato eliminato.
Dai miei test, sembra che gestureRecognizer(_:, shouldReceiveTouch:)
sia il metodo che il delegato originale sta implementando per bloccare il riconoscimento del gesto quando la barra di navigazione è nascosta, non gestureRecognizerShouldBegin(_:)
. Altre soluzioni che implementano gestureRecognizerShouldBegin(_:)
nel loro lavoro delegato a causa della mancanza di un'implementazione digestureRecognizer(_:, shouldReceiveTouch:)
causerà il comportamento predefinito di ricevere tutti i tocchi.
La soluzione di @Nathan Perry si avvicina, ma senza un'implementazione di respondsToSelector(_:)
, il codice UIKit che invia messaggi al delegato crederà che non vi sia alcuna implementazione per nessuno degli altri metodi delegato e forwardingTargetForSelector(_:)
non verrà mai chiamato.
Quindi, prendiamo il controllo di `gestureRecognizer (_ :, shouldReceiveTouch :) nello scenario specifico in cui vogliamo modificare il comportamento, e altrimenti inoltriamo tutto il resto al delegato.
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
}
}
}