Diperbarui untuk iOS 13.4
iOS 13.4 merusak solusi sebelumnya, jadi semuanya akan menjadi buruk. Sepertinya di iOS 13.4 perilaku ini sekarang dikontrol dengan metode privat _gestureRecognizer:shouldReceiveEvent:
(jangan disamakan dengan shouldReceive
metode publik baru yang ditambahkan di iOS 13.4).
Saya menemukan bahwa solusi lain yang diposting mengesampingkan delegasi, atau menyetelnya ke nol menyebabkan beberapa perilaku yang tidak terduga.
Dalam kasus saya, ketika saya berada di atas tumpukan navigasi dan mencoba menggunakan isyarat untuk memunculkan satu lagi, itu akan gagal (seperti yang diharapkan), tetapi upaya selanjutnya untuk mendorong ke tumpukan akan mulai menyebabkan gangguan grafis aneh di bilah navigasi. Ini masuk akal, karena delegasi digunakan untuk menangani lebih dari sekedar apakah akan memblokir isyarat agar tidak dikenali saat bilah navigasi disembunyikan, dan semua perilaku lain itu dibuang.
Dari pengujian saya, tampaknya itu gestureRecognizer(_:, shouldReceiveTouch:)
adalah metode yang diterapkan oleh delegasi asli untuk memblokir isyarat agar tidak dikenali saat bilah navigasi disembunyikan, bukan gestureRecognizerShouldBegin(_:)
. Solusi lain yang diimplementasikan gestureRecognizerShouldBegin(_:)
dalam pekerjaan delegasi mereka karena kurangnya implementasi gestureRecognizer(_:, shouldReceiveTouch:)
akan menyebabkan perilaku default menerima semua sentuhan.
Solusi @Nathan Perry semakin dekat, tetapi tanpa implementasi respondsToSelector(_:)
, kode UIKit yang mengirimkan pesan ke delegasi akan percaya bahwa tidak ada implementasi untuk metode delegasi lainnya, dan forwardingTargetForSelector(_:)
tidak akan pernah dipanggil.
Jadi, kami mengontrol `gestureRecognizer (_ :, shouldReceiveTouch :) dalam satu skenario tertentu yang ingin kami ubah perilakunya, dan sebaliknya meneruskan yang lainnya ke delegasi.
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
}
}
}