Como desativar o gesto de deslizar para trás no UINavigationController no iOS 7


326

No iOS 7, a Apple adicionou um novo comportamento de navegação padrão. Você pode deslizar a partir da borda esquerda da tela para voltar à pilha de navegação. Mas no meu aplicativo, esse comportamento entra em conflito com o meu menu esquerdo personalizado. Portanto, é possível desativar esse novo gesto no UINavigationController?



2
Também descobri que, se você definir navigationItem.hidesBackButton = true, esse gesto também será desativado. No meu caso, eu implementei um botão voltar personalizado e adicionei comoleftBarButtonItem
— Umair

Respostas:


586

Encontrei uma solução:

Objetivo-C:

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

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


29
Obviamente, você precisa verificar a disponibilidade de novos métodos se estiver suportando versões antigas do iOS.
— ArtFeel # 8/13

2
Existe uma maneira de desativá-lo para uma poção da vista?
— Marc

11
Você pode enable / disablereconhecer em viewDidAppear:/ viewDidDisappear. Ou, você pode implementar o UIGestureRecognizerDelegateprotocolo com sua lógica mais complexa e configurá-lo como recognizer.delegatepropriedade.
— ArtFeel #

26
Em iOS8, definindo self.navigationController.interactivePopGestureRecognizer.enabledpropriedade não funciona nos seguintes métodos de visualização: viewDidLoad, viewWillAppear, viewDidAppear, viewDidDisappear, mas obras no método viewWillDisappear. No iOS7, ele funciona em todos os métodos mencionados acima. Portanto, tente usá-lo em qualquer outro método enquanto estiver trabalhando no viewController, confirmo que funciona para mim no iOS8 quando clico em algum botão dentro da visualização.
— Sihad Begovic

8
Pode confirmar que isso não vai funcionar em iOS8 em viewDidLoad e viewWillAppear, colocá-lo em viewwilllayoutgubviews fez o truque
— tonytastic

47

Eu descobri que definir o gesto como desativado nem sempre funciona. Funciona, mas para mim só funcionou depois que uma vez usei o backgesture. Segunda vez, não desencadearia a backgesture.

A correção para mim foi delegar o gesto e implementar o método shouldbegin para retornar 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;
}

1
Obrigado! Isso é necessário para desativar completamente o furto traseiro. Ele ainda existe no iOS 8 e cheira a um bug da Apple.
— Eric Chen

Obrigado, parece ser a única coisa que funcionou.
— Ben

Eu não sei por que, mas um controlador de exibição no meu aplicativo, por algum motivo desconhecido, estava travando neste gesto de volta .. isso me impediu de encontrá-lo, pois eu não precisava desse gesto de volta e, por isso, desabilitei o uso desse código .. +1
— Ahsan Ebrahim

1
@AhsanEbrahim, quando o gesto de volta começa, viewWillAppearé chamado na exibição por trás da exibição atual. Isso pode causar estragos na lógica do código, pois a visualização atual ainda está ativa. Pode ser a causa do seu acidente.
— Phatmann 31/05

As enabledlinhas sim / não são necessárias? Você volta NOde gestureRecognizerShouldBegin, não é suficiente?
— Página Inicial>

30

Basta remover o reconhecedor de gestos do NavigationController. Trabalhe no iOS 8.

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    [self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];

a única solução que realmente funciona nos ios 8 e 9
— Kappe

7
Também funciona no iOS 10, essa deve ser a resposta aceita. A propósito, se você deseja reativá-lo, faça em [self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]algum lugar.
— ooops

22

A partir do iOS 8, a resposta aceita não funciona mais. Eu precisava interromper o swipping para descartar o gesto na tela principal do jogo, para implementar isso:

- (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;
}

2
Enquanto isso funciona com o iOS8, recebo um aviso na linha * .delegate = self; afirmando: Atribuir a id <UIGestureRecognizerDelegate>' de tipo incompatível 'ViewController * const __strong'
— David Douglas

2
A partir do iOS8, a resposta aceita ainda funciona conforme o esperado. Você provavelmente está fazendo algo errado ..
— Alexandre G

Conseguiu fazê-lo semi-funcionando chamando a resposta aceita em viewWillLayoutSubviews. No entanto, swiping causou a página a chamada 'viewDidLoad' novamente para revertido para a minha resposta acima
— Charlie Seligman

@ DavidDouglas: talvez você possa eliminar o aviso com este código: __weak __typeof (self) theSafeSelf = self? Em seguida, defina o delegado como theSafeSelf.
— lifjoy

1
@DavidDouglas: Você precisa adicionar <UIGestureRecognizerDelegate> para a interface para se livrar desse aviso
— primehalo

20

Refinei um pouco a resposta de Twan, porque:

  1. seu controlador de exibição pode ser definido como um delegado para outros reconhecedores de gestos
  2. definir o delegado para nilleva a problemas pendentes quando você volta ao controlador de visualização raiz e faz um gesto de deslizar antes de navegar para outro lugar.

O exemplo a seguir assume o 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;
}

+1 "definir o delegado como nulo leva a problemas de interrupção quando você volta ao controlador de visualização raiz e faz um gesto de deslizar antes de navegar para outro lugar".
— Albertamg 19/07/19

10

Defina isso na raiz 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;
}

9

Para Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false

12
Isso funciona, embora eu sugira o uso de encadeamento opcional em vez de forçar o desembrulhamento. por exemplo, self.navigationController? .interactivePopGestureRecognizer? .isEnabled = false
— Womble

5

funciona para mim no ios 10 e posterior:

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

}

ele não funciona no método viewDidLoad ().


5

EDITAR

Se você deseja gerenciar o recurso deslizar para trás para controladores de navegação específicos, considere usar o SwipeBack .

Com isso, você pode definir navigationController.swipeBackEnabled = NO .

Por exemplo:

#import <SwipeBack/SwipeBack.h>

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

Pode ser instalado via CocoaPods .

pod 'SwipeBack', '~> 1.0'

Peço desculpas por falta de explicação.


6
Ao promover um projeto em que você está envolvido, você deve divulgar sua afiliação.

2
Além disso, o único objetivo do seu projeto é ativar manualmente o gesto de furto quando o sistema padrão não estiver funcionando, enquanto a pergunta pergunta como desativar esse gesto em todo o sistema; portanto, mesmo se você definir self.navigationController.swipeBackEnabled = NO, tenho certeza de que isso apenas desabilitará o seu gesto . o gesto de deslizar para trás da biblioteca, mas o do sistema ainda estará ativado.

1
Desculpe pela minha resposta curta, eu acabei de editar minha resposta com informações adicionais: "útil para controladores de navegação específicos". Obrigado!
— Devxoul # 28/15

Parece usar swizzle que não é mais permitido. iOS8?
— Matt

1
@devxoul Sinto muito! Pensei ter lido algo há um tempo dizendo que não era mais permitido chiar. No entanto, não consigo encontrar nada que diga isso. Acho que estou errado.
— Matt

4

Meu método Um reconhecedor de gestos para governar todos eles:

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: não redefina o delegado em nenhum lugar da pilha de navegação: navigationController!.interactivePopGestureRecognizer!.delegate = nil


3

Este é o caminho no Swift 3

funciona para mim

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

3

Todas essas soluções manipulam o reconhecedor de gestos da Apple de uma maneira que não são recomendadas. Acabei de ser informado por um amigo que existe uma solução melhor:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

onde myPanGestureRecognizer é o reconhecedor de gestos que você está usando para, por exemplo, mostrar seu menu. Dessa forma, o reconhecedor de gestos da Apple não é ativado novamente quando você pressiona um novo controlador de navegação e não precisa contar com atrasos hacky que podem disparar muito cedo se o telefone for colocado no modo de suspensão ou sob carga pesada.

Deixando isso aqui porque sei que não me lembrarei disso da próxima vez que precisar e, então, terei a solução para o problema aqui.


3

swift 5, swift 4.2 pode usar o código abaixo.

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

2

Nenhuma das respostas fornecidas me ajudou a resolver o problema. Postando minha resposta aqui; pode ser útil para alguém

Declare private var popGesture: UIGestureRecognizer?como variável global em seu viewcontroller. Em seguida, implemente o código nos métodos viewDidAppear e 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!)
    }
}

Isso desativará o furto no iOS v8.x em diante


Estou tentando imaginar sob que circunstâncias isso funcionaria, mas o de Jack não. Você diz que tentou todas as outras respostas: o que deu errado quando você tentou o de Jack?
— Página

Por outro lado, isso parece mais simples que o de Jack, então talvez não seja importante. Decidi que eu gosto disso, porque não precisa declarar minha classe como delegada nem manipular interactivePopGestureRecognizer.delegate.
— Página

BTW, o código pode ser simplificado. Retire if( .. respondsToSelector ... A próxima linha define popGesture para um reconhecedor ou para zero. Em seguida, use o seu valor: if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture ).
— Página Inicial>

2

Isso funciona no viewDidLoad: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;
  });

Muitos dos problemas poderiam ser resolvidos com a ajuda do bom e velho dispatch_after .

Embora observe que esta solução é potencialmente insegura, use seu próprio raciocínio.

Atualizar

Para iOS 8.1, o tempo de atraso deve ser de 0,5 segundos

No iOS 9.3, não é mais necessário um atraso, ele funciona apenas colocando isso em seu viewDidLoad:
(TBD se funciona no iOS 9.0-9.3)

navigationController?.interactivePopGestureRecognizer?.enabled = false

A menos que você saiba quando o reconhecedor de gestos está instalado na exibição, aguardar um período arbitrário para desativá-lo pode ou não funcionar.
— precisa saber é

@ kalperin não é garantido que funcione, embora seja uma solução muito útil em alguns momentos. Use seu próprio raciocínio.
— 6136 Dannie P

Ele trabalha para mim ter versão maior do iOS 8.1 :)
— iChirag

viewDidLoadmais atraso é uma prática de programação arriscada. Um mau hábito para começar. E se o usuário iniciar o furto antes que sua chamada atrasada comece? Não há tempo seguro que seja garantido por tempo suficiente, mas não por muito tempo. É por isso que outras respostas, postadas muito antes da sua, sugerem a inserção do código viewDidAppear. Isso garante que tudo esteja instalado. Não invente atrasos arbitrários; use a sequência de chamadas da Apple como pretendido.
— Página

1
@iChirag true. Eu tenho notado que para 8,1 você precisa 0,5 segundos de atraso
— Dannie P

1

Para o Swift 4, isso 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
    }

}

Você não deve substituir o interativa delegado pop gesto como ele irá causar um comportamento não documentada
— Josh Bernfeld

Eu acho que ele não está realmente substituindo o delegado, mas apenas modificando a variável booleana que eles fornecidos para este fim, por isso não será um problema
— Lok SN

0

Funcionou para mim para a maioria dos controladores de exibição.

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

Não estava funcionando para alguns controladores de exibição como o UIPageViewController. No pagecontentviewcontroller do UIPageViewController, o código abaixo funcionava para mim.

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
}

Em UIGestureRecognizerDelegate,

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
   if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
      return false
}
      return true
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.