Cập nhật cho iOS 13.4
iOS 13.4 đã phá vỡ giải pháp trước đó, vì vậy mọi thứ sẽ trở nên tồi tệ. Có vẻ như trong iOS 13.4, hành vi này hiện được kiểm soát bởi một phương thức riêng tư _gestureRecognizer:shouldReceiveEvent:
(đừng nhầm lẫn với shouldReceive
phương thức công khai mới được thêm vào iOS 13.4).
Tôi nhận thấy rằng các giải pháp đã đăng khác ghi đè đại biểu hoặc đặt nó thành nil gây ra một số hành vi không mong muốn.
Trong trường hợp của tôi, khi tôi ở trên cùng của ngăn xếp điều hướng và cố gắng sử dụng cử chỉ để bật thêm một lần nữa, nó sẽ không thành công (như mong đợi), nhưng những nỗ lực tiếp theo để đẩy lên ngăn xếp sẽ bắt đầu gây ra lỗi đồ họa kỳ lạ trong thanh điều hướng. Điều này có ý nghĩa, bởi vì đại biểu đang được sử dụng để xử lý nhiều thứ hơn là chỉ chặn nhận dạng cử chỉ hay không khi thanh điều hướng bị ẩn và tất cả các hành vi khác đã bị loại bỏ.
Từ thử nghiệm của tôi, có vẻ như đó gestureRecognizer(_:, shouldReceiveTouch:)
là phương pháp mà đại biểu ban đầu đang triển khai để chặn nhận dạng cử chỉ khi thanh điều hướng bị ẩn, chứ không phải gestureRecognizerShouldBegin(_:)
. Các giải pháp khác có thể thực hiện gestureRecognizerShouldBegin(_:)
trong công việc ủy quyền của họ vì thiếu việc triển khaigestureRecognizer(_:, shouldReceiveTouch:)
sẽ gây ra hành vi mặc định là nhận tất cả các liên lạc.
Giải pháp của @Nathan Perry đã gần đạt được, nhưng không có triển khai respondsToSelector(_:)
, mã UIKit gửi thông báo đến người được ủy quyền sẽ tin rằng không có triển khai nào cho bất kỳ phương thức ủy quyền nào khác vàforwardingTargetForSelector(_:)
sẽ không bao giờ được gọi.
Vì vậy, chúng tôi nắm quyền kiểm soát `scrollRecognizer (_ :, shouldReceiveTouch :) trong một kịch bản cụ thể mà chúng tôi muốn sửa đổi hành vi và chuyển tiếp mọi thứ khác tới đại biểu.
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
}
}
}