Zaktualizowano dla iOS 13.4
iOS 13.4 zepsuł poprzednie rozwiązanie, więc sprawy potoczą się brzydko. Wygląda na to, że w iOS 13.4 to zachowanie jest teraz kontrolowane przez metodę prywatną _gestureRecognizer:shouldReceiveEvent:
(nie mylić z nową shouldReceive
metodą publiczną dodaną w iOS 13.4).
Okazało się, że inne opublikowane rozwiązania zastępujące delegata lub ustawienie go na zero spowodowały nieoczekiwane zachowanie.
W moim przypadku, gdy byłem na szczycie stosu nawigacyjnego i próbowałem użyć gestu, aby wyskoczyć jeszcze jeden, kończyło się to niepowodzeniem (zgodnie z oczekiwaniami), ale kolejne próby wepchnięcia na stos zaczęłyby powodować dziwne graficzne usterki w Pasek nawigacyjny. Ma to sens, ponieważ delegat jest używany do obsługi czegoś więcej niż tylko tego, czy blokować rozpoznawanie gestu, gdy pasek nawigacji jest ukryty, a wszystkie inne zachowania były odrzucane.
Z moich testów wynika, że gestureRecognizer(_:, shouldReceiveTouch:)
jest to metoda, którą implementuje oryginalny delegat, aby zablokować rozpoznawanie gestu, gdy pasek nawigacji jest ukryty, a nie gestureRecognizerShouldBegin(_:)
. Inne rozwiązania, które zaimplementują gestureRecognizerShouldBegin(_:)
w swojej pracy delegata, ponieważ brak implementacji gestureRecognizer(_:, shouldReceiveTouch:)
spowoduje domyślne zachowanie wszystkich dotknięć.
Rozwiązanie @Nathana Perry'ego zbliża się, ale bez implementacji respondsToSelector(_:)
kod UIKit, który wysyła wiadomości do delegata, będzie uważał, że nie ma implementacji dla żadnej innej metody delegata i forwardingTargetForSelector(_:)
nigdy nie zostanie wywołany.
Więc przejmujemy kontrolę nad `gestRecognizer (_ :, shouldReceiveTouch :) w jednym konkretnym scenariuszu, w którym chcemy zmodyfikować zachowanie, iw przeciwnym razie przekazujemy wszystko inne do delegata.
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
}
}
}