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 NavigationBar
deslizamiento de la bandeja táctil, pero aún así gesture
. Puedo escribir gestos personalizados, pero prefiero no hacerlo y confiar en el UINavigationController
deslizamiento 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 NavigationBar
y seguir teniendo el deslizamiento?
Respuestas:
Un truco que está trabajando es establecer la
interactivePopGestureRecognizer
's delegado de laUINavigationController
denil
la siguiente manera:[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
Pero en algunas situaciones podría crear efectos extraños.
fuente
gestureRecognizerShouldBegin
, volviendotrue
si elnavigationController
'sviewController
recuento es mayor que 0.viewWillDisappear
y hasta ahora no ha experimentado efectos secundarios adversos.Problemas con otros métodos
Establecer el
interactivePopGestureRecognizer.delegate = nil
tiene efectos secundarios no deseados.La configuración
navigationController?.navigationBar.hidden = true
funciona, 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
UIGestureRecognizerDelegate
para su controlador de navegación. Establecerlo en un controlador en laUINavigationController
pila es lo que está causando losEXC_BAD_ACCESS
errores.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.delegate
en una instancia de su nuevaInteractivePopRecognizer
clase.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_ACCESS
UIGestureRecognizerDelegate
al 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,gestureRecognizerShouldBegin
no se llamó. Así que coloqué el.delegate = self
inviewDidAppear()
. Eso solucionó los extraños efectos en mi caso .. ¡Salud!EXEC_BAD_ACCESS
sucede?EXC_BAD_ACCESS
error: 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 nuevoshouldReceive
mé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.navigationController
era una fuerte referencia en AlwaysPoppableDelegate. He editado el código para que sea unaweak
referencia.Puede subclase UINavigationController de la siguiente manera:
@interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @end
Implementació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; } @end
fuente
UIPageViewController
desplazamiento.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,initialInteractivePopGestureRecognizerDelegate
ya quenavigationController
podría ser nulo allí (aún no se ha colocado en la pila).viewWillAppear
del 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; } @end
fuente
Mi solución es extender directamente la
UINavigationController
clase: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
viewDidAppear
que 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 = bridgingDelegate
Delegado 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
UINavigationBar
y no perderswipe to back gesture
. He probado en iOS 13.3.1 y 12.4.3 y funciona.Necesita crear una clase personalizada de
UINavigationController
y configurar esa clase paraUINavigationController
enStoryboard
NO esconda el
NavigationBar
en elStoryboard
Ejemplo de
Storyboard
:Y finalmente, pon esto:
navigationBar.isHidden = true
enviewDidLoad
deCustomNavigationController
clase.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.1
y deslizar hacia atrás funciona.Respuesta de Xamarin:
Implemente la
IUIGestureRecognizerDelegate
interfaz en la definición de clase de su ViewController:public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate
En 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
UINavigationController
controlador de vista raíz de? Me saleEXEC_BAD_ACCESS
cuando 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
setNavigationBarHidden
método con animated en suYES
lugar.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