Cómo deshabilitar el gesto de deslizar hacia atrás en UINavigationController en iOS 7

326

En iOS 7, Apple agregó un nuevo comportamiento de navegación predeterminado. Puede deslizar desde el borde izquierdo de la pantalla para volver a la pila de navegación. Pero en mi aplicación, este comportamiento entra en conflicto con mi menú izquierdo personalizado. Entonces, ¿es posible deshabilitar este nuevo gesto en UINavigationController?

ArtFeel
fuente
2
También descubrí que si configuras navigationItem.hidesBackButton = true, este gesto también se desactiva. En mi caso, implementé un botón de retroceso personalizado y lo agregué comoleftBarButtonItem
Umair

Respuestas:

586

Encontré una solución:

C objetivo:

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}

Swift 3+:
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

ArtFeel
fuente
29
Por supuesto, debe verificar la disponibilidad de nuevos métodos si admite versiones antiguas de iOS.
ArtFeel
2
¿Hay alguna forma de desactivarlo para una poción de la vista?
Marc
11
Puede enable / disablereconocer en viewDidAppear:/ viewDidDisappear. O bien, puede implementar el UIGestureRecognizerDelegateprotocolo con su lógica más compleja y establecerlo como recognizer.delegatepropiedad.
ArtFeel
26
En iOS8, el establecimiento de self.navigationController.interactivePopGestureRecognizer.enabledla propiedad no funciona en los siguientes métodos de vista: viewDidLoad, viewWillAppear, viewDidAppear, viewDidDisappear, pero las obras en el método viewWillDisappear. En iOS7 funciona en todos los métodos mencionados anteriormente. Así que trate de usarlo en cualquier otro método mientras trabaja en viewController, confirmo que funciona para mí en iOS8 cuando hago clic en algún botón dentro de la vista.
Sihad Begovic
8
Puede confirmar que esto no funcionará en iOS8 en viewDidLoad y viewWillAppear, ponerlo a la vistawilllayoutgubviews hizo el truco
tonytastic
47

Descubrí que configurar el gesto en deshabilitado solo no siempre funciona. Funciona, pero para mí solo funcionó después de usar una vez el backgesture. La segunda vez no activaría el backgesture.

La solución para mí fue delegar el gesto e implementar el método shouldbegin para devolver NO:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    // Disable iOS 7 back gesture
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
        self.navigationController.interactivePopGestureRecognizer.delegate = self;
    }
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];

    // Enable iOS 7 back gesture
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.enabled = YES;
        self.navigationController.interactivePopGestureRecognizer.delegate = nil;
    }
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    return NO;
}
Antoine
fuente
1
¡Gracias! Esto es necesario para deshabilitar completamente el deslizamiento hacia atrás. Todavía existe en iOS 8 y huele a un error de Apple.
Eric Chen
Gracias, parece ser lo único que ha funcionado.
Ben
No sé por qué, pero un controlador de vista en mi aplicación por alguna razón desconocida se estaba bloqueando en este gesto hacia atrás ... esto me salvó de encontrarlo, ya que no necesitaba este gesto hacia atrás y desactivé el uso de este código ... +1
Ahsan Ebrahim
1
@AhsanEbrahim, cuando comienza el gesto de retroceso, viewWillAppearaparece en la vista detrás de la vista actual. Esto puede causar estragos en la lógica del código ya que la vista actual todavía está activa. Podría ser la causa de tu accidente.
phatmann
¿Son enablednecesarias las líneas sí / no? Regresas NOde gestureRecognizerShouldBegin, ¿no es eso suficiente?
ToolmakerSteve
30

Simplemente quite el reconocedor de gestos de NavigationController. Trabaja en iOS 8.

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    [self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];
Vladimir Samoylov
fuente
la única solución que realmente funciona en iOS 8 y 9
Kappe
77
También funciona en iOS 10, esta debería ser la respuesta aceptada. Por cierto, si quieres volver a habilitarlo, hazlo en [self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]alguna parte.
ooops
22

A partir de iOS 8, la respuesta aceptada ya no funciona. Necesitaba detener el movimiento para descartar el gesto en la pantalla principal de mi juego, así que implementé esto:

- (void)viewDidAppear:(BOOL)animated
{
     [super viewDidAppear:animated];

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.delegate = self;
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.delegate = nil;
    }

}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
     return NO;
}
Charlie Seligman
fuente
2
Si bien esto funciona con iOS8, aparece una advertencia en la línea * .delegate = self; indicando: Asignando a id <UIGestureRecognizerDelegate> 'del tipo incompatible' ViewController * const __strong '
David Douglas
2
A partir de iOS8, la respuesta aceptada sigue funcionando como se esperaba. Probablemente estés haciendo algo mal ...
Alexandre G
Se las arregló para que funcionara de manera semi llamada la respuesta aceptada en viewWillLayoutSubviews. Sin embargo, deslizar hizo que la página volviera a llamar 'viewDidLoad', así que volví a mi respuesta anterior
Charlie Seligman,
@DavidDouglas: quizás podría eliminar la advertencia con este código: __weak __typeof (self) theSafeSelf = self? Luego configure el delegado a theSafeSelf.
Lifjoy
1
@DavidDouglas: debe agregar <UIGestureRecognizerDelegate> a la interfaz para deshacerse de esa advertencia
primehalo
20

He refinado un poco la respuesta de Twan porque:

  1. su controlador de vista se puede configurar como delegado a otros reconocedores de gestos
  2. configurar el delegado nillleva a problemas de bloqueo cuando vuelve al controlador de vista raíz y hace un gesto de deslizamiento antes de navegar a otra parte.

El siguiente ejemplo supone iOS 7:

{
    id savedGestureRecognizerDelegate;
}

- (void)viewWillAppear:(BOOL)animated
{
    savedGestureRecognizerDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
    self.navigationController.interactivePopGestureRecognizer.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated
{
    self.navigationController.interactivePopGestureRecognizer.delegate = savedGestureRecognizerDelegate;
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
        return NO;
    }
    // add whatever logic you would otherwise have
    return YES;
}
Jack
fuente
+1 "configurar el delegado a cero conduce a problemas de bloqueo cuando vuelve al controlador de vista raíz y hace un gesto de deslizamiento antes de navegar a otro lado".
albertamg
10

Configure esto en root vc:

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:YES];
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;

}

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:YES];
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
reza_khalafi
fuente
9

Para Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false
iPhone 7
fuente
12
Esto funciona, aunque sugeriría usar encadenamiento opcional en lugar de desenvolver la fuerza. ej. self.navigationController? .interactivePopGestureRecognizer? .isEnabled = false
Womble
5

A mí me funciona en iOS 10 y posteriores:

- (void)viewWillAppear:(BOOL)animated {
    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    }

}

no funciona en el método viewDidLoad ().

Lógica
fuente
5

EDITAR

Si desea administrar la función de deslizar hacia atrás para controladores de navegación específicos, considere usar SwipeBack .

Con esto, puedes configurar navigationController.swipeBackEnabled = NO .

Por ejemplo:

#import <SwipeBack/SwipeBack.h>

- (void)viewWillAppear:(BOOL)animated
{
    navigationController.swipeBackEnabled = NO;
}

Se puede instalar a través de CocoaPods .

pod 'SwipeBack', '~> 1.0'

Pido disculpas por falta de explicación.

devxoul
fuente
66
Al promocionar un proyecto en el que esté involucrado, debe divulgar su afiliación con él.
2
Además, el único propósito de su proyecto es habilitar manualmente el gesto de deslizar cuando el sistema predeterminado no funciona, mientras que la pregunta es cómo deshabilitar ese gesto de todo el sistema, por lo que incluso si configura self.navigationController.swipeBackEnabled = NO, estoy bastante seguro de que esto solo deshabilitará su gesto de deslizar hacia atrás de la biblioteca, pero el sistema todavía estará habilitado.
1
Lo siento por mi breve respuesta, acabo de editar mi respuesta con información adicional: "útil para controladores de navegación específicos". ¡Gracias!
devxoul
Parece usar swizzle que ya no está permitido. iOS8?
Matt
1
@devxoul lo siento! Pensé que había leído algo hace un tiempo diciendo que ya no se permitía el swizzling. Sin embargo, no puedo encontrar nada que diga esto. Supongo que estoy equivocado.
Matt
4

Mi metodo Un reconocedor de gestos para gobernarlos a todos:

class DisabledGestureViewController: UIViewController: UIGestureRecognizerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController!.interactivePopGestureRecognizer!.delegate = self
    }

    func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
        // Prevent going back to the previous view
        return !(navigationController!.topViewController is DisabledGestureViewController)
    }
}

Importante: no reinicie el delegado en ningún lugar de la pila de navegación: navigationController!.interactivePopGestureRecognizer!.delegate = nil

SoftDesigner
fuente
3

Este es el camino en Swift 3

funciona para mi

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
Tayo119
fuente
3

Todas estas soluciones manipulan el reconocedor de gestos de Apple de una manera que no recomiendan. Un amigo me acaba de decir que hay una mejor solución:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

donde myPanGestureRecognizer es el reconocedor de gestos que está utilizando para, por ejemplo, mostrar su menú. De esa forma, el gestor de reconocimiento de gestos de Apple no se vuelve a activar cuando presionas un nuevo controlador de navegación y no necesitas depender de retrasos extravagantes que pueden activarse demasiado temprano si tu teléfono se pone en suspensión o bajo una carga pesada.

Dejando esto aquí porque sé que no recordaré esto la próxima vez que lo necesite, y luego tendré la solución al problema aquí.

uli testigo
fuente
3

swift 5, swift 4.2 puede usar el código de abajo.

// disable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// enable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
Zgpeace
fuente
2

Ninguna de las respuestas dadas me ayudó a resolver el problema. Publicando mi respuesta aquí; puede ser útil para alguien

Declare private var popGesture: UIGestureRecognizer?como variable global en su viewcontroller. Luego implemente el código en los métodos viewDidAppear y viewWillDisappear

override func viewDidAppear(animated: Bool) {

    super.viewDidAppear(animated)

    if self.navigationController!.respondsToSelector(Selector("interactivePopGestureRecognizer")) {

        self.popGesture = navigationController!.interactivePopGestureRecognizer
        self.navigationController!.view.removeGestureRecognizer(navigationController!.interactivePopGestureRecognizer!)
    }
}


override func viewWillDisappear(animated: Bool) {

    super.viewWillDisappear(animated)

    if self.popGesture != nil {
        navigationController!.view.addGestureRecognizer(self.popGesture!)
    }
}

Esto deshabilitará deslizar hacia atrás en iOS v8.x en adelante

Augustine PA
fuente
Estoy tratando de imaginar en qué circunstancias funcionaría esto, pero Jack no lo haría. Dices que intentaste todas las otras respuestas: ¿qué salió mal cuando probaste las de Jack?
ToolmakerSteve
Por otro lado, esto parece más simple que el de Jack, por lo que tal vez no sea importante. Decidí que me gusta esto, porque no tengo que declarar a mi clase como delegado, ni manipularlo interactivePopGestureRecognizer.delegate.
ToolmakerSteve
Por cierto, el código se puede simplificar. Remover if( .. respondsToSelector ... La siguiente línea establece popGesture en un reconocedor o en cero. A continuación, utilice su valor: if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture ).
ToolmakerSteve
2

Esto funciona viewDidLoad:para iOS 8:

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      self.navigationController.interactivePopGestureRecognizer.enabled = false;
  });

Muchos de los problemas podrían resolverse con la ayuda del buen ol ' dispatch_after .

Aunque tenga en cuenta que esta solución es potencialmente insegura, utilice su propio razonamiento.

Actualizar

Para iOS 8.1, el tiempo de retraso debe ser de 0,5 segundos

En iOS 9.3 ya no se necesita demora, funciona simplemente colocando esto en su viewDidLoad:
(TBD si funciona en iOS 9.0-9.3)

navigationController?.interactivePopGestureRecognizer?.enabled = false
Dannie P
fuente
A menos que sepa cuándo se instala el reconocedor de gestos en la vista, esperar un tiempo arbitrario para desactivarlo puede o no funcionar.
kalperin
@kalperin no está garantizado que funcione, aunque en ocasiones es una solución muy útil. Usa tu propio razonamiento.
Dannie P
Funciona para mí tener una versión superior a iOS 8.1 :)
iChirag
viewDidLoadAdemás el retraso es una práctica de programación arriesgada. Un mal hábito para comenzar. ¿Qué sucede si el usuario inicia el deslizamiento antes de que se inicie su llamada demorada? No hay un tiempo seguro que se garantice que sea lo suficientemente largo, pero no demasiado. Es por eso que otras respuestas, publicadas mucho antes que la suya, sugieren colocar el código viewDidAppear. Eso asegura que todo esté instalado. No invente retrasos arbitrarios; use la secuencia de llamadas de Apple según lo previsto.
ToolmakerSteve
1
@iChirag cierto. He notado que para 8.1 necesitas 0.5 segundos de retraso
Dannie P
1

Para Swift 4 esto funciona:

class MyViewController: UIViewController, UIGestureRecognizerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationController?.interactivePopGestureRecognizer?.gesture.delegate = self
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)

        self.navigationController?.interactivePopGestureRecognizer?.gesture.isEnabled = false
    }

}
Mat0
fuente
No debe anular el delegado interactivo de gestos pop, ya que causará un comportamiento indocumentado
Josh Bernfeld
Creo que en realidad no está anulando al delegado, sino simplemente modificando la variable booleana que han proporcionado para este mismo propósito, por lo que no será un problema
Lok SN
0

Funcionó para mí para la mayoría de los controladores de vista.

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

No funcionaba para algunos controladores de vista como UIPageViewController. En la página UIPageViewControllercontentviewcontroller debajo del código funcionó para mí.

override func viewDidLoad() {
   self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
   self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}
override func viewWillDisappear(_ animated: Bool) {
   self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
   self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
}

En UIGestureRecognizerDelegate,

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
   if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
      return false
}
      return true
}
Faris Muhammed
fuente