Me encanta el paquete de deslizamiento que se hereda de incrustar sus vistas en un archivo UINavigationController. Desafortunadamente, parece que no puedo encontrar una manera de ocultar el NavigationBardeslizamiento de la bandeja táctil, pero aún así gesture. Puedo escribir gestos personalizados, pero prefiero no hacerlo y confiar en el UINavigationControllerdeslizamiento hacia atrás gesture.
si lo desmarco en el guión gráfico, el deslizamiento hacia atrás no funciona

alternativamente, si lo oculto programáticamente, el mismo escenario.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES
}
¿No hay forma de ocultar la parte superior NavigationBary seguir teniendo el deslizamiento?

Respuestas:
Un truco que está trabajando es establecer la
interactivePopGestureRecognizer's delegado de laUINavigationControllerdenilla siguiente manera:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];Pero en algunas situaciones podría crear efectos extraños.
fuente
gestureRecognizerShouldBegin, volviendotruesi elnavigationController'sviewControllerrecuento es mayor que 0.viewWillDisappeary hasta ahora no ha experimentado efectos secundarios adversos.Problemas con otros métodos
Establecer el
interactivePopGestureRecognizer.delegate = niltiene efectos secundarios no deseados.La configuración
navigationController?.navigationBar.hidden = truefunciona, pero no permite ocultar los cambios en la barra de navegación.Por último, generalmente es una mejor práctica crear un objeto de modelo que sea
UIGestureRecognizerDelegatepara su controlador de navegación. Establecerlo en un controlador en laUINavigationControllerpila es lo que está causando losEXC_BAD_ACCESSerrores.Solución completa
Primero, agregue esta clase a su proyecto:
class InteractivePopRecognizer: NSObject, UIGestureRecognizerDelegate { var navigationController: UINavigationController init(controller: UINavigationController) { self.navigationController = controller } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return navigationController.viewControllers.count > 1 } // This is necessary because without it, subviews of your top controller can // cancel out your gesture recognizer on the edge. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } }Luego, configure su controlador de navegación
interactivePopGestureRecognizer.delegateen una instancia de su nuevaInteractivePopRecognizerclase.var popRecognizer: InteractivePopRecognizer? override func viewDidLoad() { super.viewDidLoad() setInteractiveRecognizer() } private func setInteractiveRecognizer() { guard let controller = navigationController else { return } popRecognizer = InteractivePopRecognizer(controller: controller) controller.interactivePopGestureRecognizer?.delegate = popRecognizer }Disfrute de una barra de navegación oculta sin efectos secundarios, que funciona incluso si su controlador superior tiene subvistas de vista de tabla, colección o desplazamiento.
fuente
En mi caso, para evitar efectos extraños
Controlador de vista raíz
override func viewDidLoad() { super.viewDidLoad() // Enable swipe back when no navigation bar navigationController?.interactivePopGestureRecognizer?.delegate = self } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if(navigationController!.viewControllers.count > 1){ return true } return false }http://www.gampood.com/pop-viewcontroller-with-out-navigation-bar/
fuente
EXEC_BAD_ACCESSUIGestureRecognizerDelegateal controlador de vista raíz ... En mi caso, el delegado se configuró en nulo en un controlador de vista posterior al controlador de vista raíz, por lo que cuando se devolvió al controlador de vista raíz,gestureRecognizerShouldBeginno se llamó. Así que coloqué el.delegate = selfinviewDidAppear(). Eso solucionó los extraños efectos en mi caso .. ¡Salud!EXEC_BAD_ACCESSsucede?EXC_BAD_ACCESSerror: stackoverflow.com/questions/28746123/…Actualizado para iOS 13.4
iOS 13.4 rompió la solución anterior, por lo que las cosas se pondrán feas. Parece que en iOS 13.4 este comportamiento ahora está controlado por un método privado
_gestureRecognizer:shouldReceiveEvent:(no debe confundirse con el nuevoshouldReceivemétodo público agregado en iOS 13.4).Descubrí que otras soluciones publicadas que anulaban al delegado o lo configuraban como nulo causaron un comportamiento inesperado.
En mi caso, cuando estaba en la parte superior de la pila de navegación e intenté usar el gesto para hacer estallar uno más, fallaría (como se esperaba), pero los intentos posteriores de empujar hacia la pila comenzarían a causar fallas gráficas extrañas en el barra de navegación. Esto tiene sentido, porque el delegado se está utilizando para manejar más que solo si se debe bloquear o no el gesto para que no se reconozca cuando la barra de navegación está oculta y todo ese otro comportamiento se descarta.
Según mis pruebas, parece que
gestureRecognizer(_:, shouldReceiveTouch:)es el método que está implementando el delegado original para evitar que el gesto sea reconocido cuando la barra de navegación está oculta, nogestureRecognizerShouldBegin(_:). Otras soluciones que implementangestureRecognizerShouldBegin(_:)en su trabajo delegado debido a la falta de una implementación degestureRecognizer(_:, shouldReceiveTouch:)provocará el comportamiento predeterminado de recibir todos los toques.La solución de @Nathan Perry se acerca, pero sin una implementación de
respondsToSelector(_:), el código UIKit que envía mensajes al delegado creerá que no hay implementación para ninguno de los otros métodos de delegado, yforwardingTargetForSelector(_:)nunca será llamado.Por lo tanto, tomamos el control de `GestureRecognizer (_ :, shouldReceiveTouch :) en el escenario específico en el que queremos modificar el comportamiento y, de lo contrario, reenviar todo lo demás al 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 } // For handling iOS before 13.4 @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 } } // For handling iOS 13.4+ @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, *) { // iOS 13.4+ does not need to override responds(to:) behavior, it only uses forwardingTarget 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 } } }fuente
gestureRecognizerShouldBegin:cosa, y "parece funcionar". Me pregunto si debería estar atento.navigationControllerera una fuerte referencia en AlwaysPoppableDelegate. He editado el código para que sea unaweakreferencia.Puede subclase UINavigationController de la siguiente manera:
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @endImplementación:
@implementation CustomNavigationController - (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated { [super setNavigationBarHidden:hidden animated:animated]; self.interactivePopGestureRecognizer.delegate = self; } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (self.viewControllers.count > 1) { return YES; } return NO; } @endfuente
UIPageViewControllerdesplazamiento.Respuesta simple, sin efectos secundarios
Si bien la mayoría de las respuestas aquí son buenas, aparentemente tienen efectos secundarios no deseados (ruptura de la aplicación) o son detalladas.
La solución más simple pero funcional que pude encontrar fue la siguiente:
En el ViewController que está ocultando la barra de navegación,
class MyNoNavBarViewController: UIViewController { // needed for reference when leaving this view controller var initialInteractivePopGestureRecognizerDelegate: UIGestureRecognizerDelegate? override func viewDidLoad() { super.viewDidLoad() // we will need a reference to the initial delegate so that when we push or pop.. // ..this view controller we can appropriately assign back the original delegate initialInteractivePopGestureRecognizerDelegate = self.navigationController?.interactivePopGestureRecognizer?.delegate } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(true) // we must set the delegate to nil whether we are popping or pushing to.. // ..this view controller, thus we set it in viewWillAppear() self.navigationController?.interactivePopGestureRecognizer?.delegate = nil } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(true) // and every time we leave this view controller we must set the delegate back.. // ..to what it was originally self.navigationController?.interactivePopGestureRecognizer?.delegate = initialInteractivePopGestureRecognizerDelegate } }Otras respuestas han sugerido simplemente establecer el delegado en cero. Al deslizar el dedo hacia atrás hasta el controlador de vista inicial en la pila de navegación, se desactivan todos los gestos. Algún tipo de supervisión, quizás, de los desarrolladores de UIKit / UIGesture.
Además, algunas respuestas aquí que he implementado dieron como resultado un comportamiento de navegación de Apple no estándar (específicamente, lo que permite la capacidad de desplazarse hacia arriba o hacia abajo mientras también desliza hacia atrás). Estas respuestas también parecen un poco detalladas y, en algunos casos, incompletas.
fuente
viewDidLoad()no es un buen lugar para capturar,initialInteractivePopGestureRecognizerDelegateya quenavigationControllerpodría ser nulo allí (aún no se ha colocado en la pila).viewWillAppeardel lugar donde se esconde la barra de navegación sería más apropiadoPartiendo de la respuesta de Hunter Maximillion Monk , creé una subclase para UINavigationController y luego configuré la clase personalizada para mi UINavigationController en mi guión gráfico. El código final para las dos clases se ve así:
InteractivePopRecognizer:
class InteractivePopRecognizer: NSObject { // MARK: - Properties fileprivate weak var navigationController: UINavigationController? // MARK: - Init init(controller: UINavigationController) { self.navigationController = controller super.init() self.navigationController?.interactivePopGestureRecognizer?.delegate = self } } extension InteractivePopRecognizer: UIGestureRecognizerDelegate { func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return (navigationController?.viewControllers.count ?? 0) > 1 } // This is necessary because without it, subviews of your top controller can cancel out your gesture recognizer on the edge. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } }HiddenNavBarNavigationController:
class HiddenNavBarNavigationController: UINavigationController { // MARK: - Properties private var popRecognizer: InteractivePopRecognizer? // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() setupPopRecognizer() } // MARK: - Setup private func setupPopRecognizer() { popRecognizer = InteractivePopRecognizer(controller: self) } }Guión gráfico:
fuente
Parece que la solución proporcionada por @ChrisVasseli es la mejor. Me gustaría proporcionar la misma solución en Objective-C porque la pregunta es sobre Objective-C (ver etiquetas)
@interface InteractivePopGestureDelegate : NSObject <UIGestureRecognizerDelegate> @property (nonatomic, weak) UINavigationController *navigationController; @property (nonatomic, weak) id<UIGestureRecognizerDelegate> originalDelegate; @end @implementation InteractivePopGestureDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { if (self.navigationController.navigationBarHidden && self.navigationController.viewControllers.count > 1) { return YES; } else { return [self.originalDelegate gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch]; } } - (BOOL)respondsToSelector:(SEL)aSelector { if (aSelector == @selector(gestureRecognizer:shouldReceiveTouch:)) { return YES; } else { return [self.originalDelegate respondsToSelector:aSelector]; } } - (id)forwardingTargetForSelector:(SEL)aSelector { return self.originalDelegate; } @end @interface NavigationController () @property (nonatomic) InteractivePopGestureDelegate *interactivePopGestureDelegate; @end @implementation NavigationController - (void)viewDidLoad { [super viewDidLoad]; self.interactivePopGestureDelegate = [InteractivePopGestureDelegate new]; self.interactivePopGestureDelegate.navigationController = self; self.interactivePopGestureDelegate.originalDelegate = self.interactivePopGestureRecognizer.delegate; self.interactivePopGestureRecognizer.delegate = self.interactivePopGestureDelegate; } @endfuente
Mi solución es extender directamente la
UINavigationControllerclase:import UIKit extension UINavigationController: UIGestureRecognizerDelegate { override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.interactivePopGestureRecognizer?.delegate = self } public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return self.viewControllers.count > 1 } }De esta forma, todos los controladores de navegación se descartarán deslizándolos.
fuente
viewDidAppearque se ignoren todas las llamadas en los VC que pertenecen a cualquier controlador de navegación.Puede hacerlo con un Delegado Proxy. Cuando esté construyendo el controlador de navegación, tome el delegado existente. Y páselo al proxy. Luego, pase todos los métodos de delegado al delegado existente, excepto el
gestureRecognizer:shouldReceiveTouch:usoforwardingTargetForSelector:Preparar:
let vc = UIViewController(nibName: nil, bundle: nil) let navVC = UINavigationController(rootViewController: vc) let bridgingDelegate = ProxyDelegate() bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegateDelegado apoderado:
class ProxyDelegate: NSObject, UIGestureRecognizerDelegate { var existingDelegate: UIGestureRecognizerDelegate? = nil override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? { return existingDelegate } func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool { return true } }fuente
La respuesta de Hunter Monk es realmente asombrosa, pero desafortunadamente en iOS 13.3.1, no funciona.
Te explicare otra forma de esconderte
UINavigationBary no perderswipe to back gesture. He probado en iOS 13.3.1 y 12.4.3 y funciona.Necesita crear una clase personalizada de
UINavigationControllery configurar esa clase paraUINavigationControllerenStoryboardNO esconda el
NavigationBaren elStoryboardEjemplo de
Storyboard:Y finalmente, pon esto:
navigationBar.isHidden = trueenviewDidLoaddeCustomNavigationControllerclase.Asegúrese de NO utilizar este método
setNavigationBarHidden(true, animated: true)para ocultar elNavigationBar.import UIKit class CustomNavigationController: UINavigationController { override func viewDidLoad() { super.viewDidLoad() navigationBar.isHidden = true } }fuente
iOS 13.4.1y deslizar hacia atrás funciona.Respuesta de Xamarin:
Implemente la
IUIGestureRecognizerDelegateinterfaz en la definición de clase de su ViewController:public partial class myViewController : UIViewController, IUIGestureRecognizerDelegateEn su ViewController agregue el siguiente método:
[Export("gestureRecognizerShouldBegin:")] public bool ShouldBegin(UIGestureRecognizer recognizer) { if (recognizer is UIScreenEdgePanGestureRecognizer && NavigationController.ViewControllers.Length == 1) { return false; } return true; }En su ViewController's
ViewDidLoad()agregue la siguiente línea:NavigationController.InteractivePopGestureRecognizer.Delegate = this;fuente
UINavigationControllercontrolador de vista raíz de? Me saleEXEC_BAD_ACCESScuando intento esto.gestureRecognizerShouldBegin:.Probé esto y está funcionando perfectamente: Cómo ocultar la barra de navegación sin perder la capacidad de deslizamiento
La idea es implementar "UIGestureRecognizerDelegate" en su .hy agregar esto a su archivo .m.
- (void)viewWillAppear:(BOOL)animated { // hide nav bar [[self navigationController] setNavigationBarHidden:YES animated:YES]; // enable slide-back if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = YES; self.navigationController.interactivePopGestureRecognizer.delegate = self; } } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return YES; }fuente
Aquí está mi solución: estoy cambiando alfa en la barra de navegación, pero la barra de navegación no está oculta. Todos mis controladores de vista son una subclase de mi BaseViewController, y ahí tengo:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) navigationController?.navigationBar.alpha = 0.0 }También puede subclasificar UINavigationController y poner ese método allí.
fuente
Algunas personas han tenido éxito llamando al
setNavigationBarHiddenmétodo con animated en suYESlugar.fuente
En mi controlador de vista sin barra de navegación utilizo
open override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) CATransaction.begin() UIView.animate(withDuration: 0.25, animations: { [weak self] in self?.navigationController?.navigationBar.alpha = 0.01 }) CATransaction.commit() } open override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) CATransaction.begin() UIView.animate(withDuration: 0.25, animations: { [weak self] in self?.navigationController?.navigationBar.alpha = 1.0 }) CATransaction.commit() }Sin embargo, durante el despido interactivo, el botón de retroceso brillará, por eso lo oculté.
fuente
Hay una solución realmente simple que probé y funciona perfectamente, está en Xamarin.iOS pero también se puede aplicar al nativo:
public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); this.NavigationController.SetNavigationBarHidden(true, true); } public override void ViewDidAppear(bool animated) { base.ViewDidAppear(animated); this.NavigationController.SetNavigationBarHidden(false, false); this.NavigationController.NavigationBar.Hidden = true; } public override void ViewWillDisappear(bool animated) { base.ViewWillDisappear(animated); this.NavigationController.SetNavigationBarHidden(true, false); }fuente
Aquí se explica cómo deshabilitar el reconocedor de gestos cuando el usuario se desliza fuera del ViewController. Puede pegarlo en sus métodos viewWillAppear () o en sus métodos ViewDidLoad ().
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; }fuente