Atualizado para iOS 13.4
O iOS 13.4 quebrou a solução anterior, então as coisas vão ficar feias. Parece que no iOS 13.4 esse comportamento agora é controlado por um método privado _gestureRecognizer:shouldReceiveEvent:
(não deve ser confundido com o novo shouldReceive
método público adicionado no iOS 13.4).
Descobri que outras soluções postadas substituindo o delegado ou definindo-o como nulo causaram algum comportamento inesperado.
No meu caso, quando eu estava no topo da pilha de navegação e tentava usar o gesto para abrir mais uma, ele falharia (como esperado), mas as tentativas subsequentes de empurrar para a pilha começariam a causar falhas gráficas estranhas no Barra de navegação. Isso faz sentido, porque o delegado está sendo usado para lidar com mais do que apenas bloquear ou não o gesto de ser reconhecido quando a barra de navegação está oculta e todos os outros comportamentos foram descartados.
Pelo meu teste, parece que gestureRecognizer(_:, shouldReceiveTouch:)
é o método que o delegado original está implementando para bloquear o gesto de ser reconhecido quando a barra de navegação está oculta, não gestureRecognizerShouldBegin(_:)
. Outras soluções que implementam gestureRecognizerShouldBegin(_:)
em seus delegados funcionam porque a falta de uma implementação degestureRecognizer(_:, shouldReceiveTouch:)
causará o comportamento padrão de receber todos os toques.
A solução de @Nathan Perry chega perto, mas sem uma implementação respondsToSelector(_:)
, o código UIKit que envia mensagens para o delegado acreditará que não há implementação para nenhum dos outros métodos delegados e forwardingTargetForSelector(_:)
nunca será chamado.
Portanto, assumimos o controle de `gestorRecognizer (_ :, deveriaReceberTouch :) em um cenário específico que desejamos modificar o comportamento e, caso contrário, encaminharemos todo o resto para o delegado.
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
}
}
}