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?

ArtFeel
fonte
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

ArtFeel
fonte
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;
}
Antoine
fonte
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];
Vladimir Samoylov
fonte
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;
}
Charlie Seligman
fonte
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;
}
Ja͢ck
fonte
+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;
}
reza_khalafi
fonte
9

Para Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false
iPhone 7
fonte
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 ().

Lógica
fonte
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.

devxoul
fonte
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

SoftDesigner
fonte
3

Este é o caminho no Swift 3

funciona para mim

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
Tayo119
fonte
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.

uliwitness
fonte
3

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

// disable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// enable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
Zgpeace
fonte
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

Agostinho PA
fonte
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
Dannie P
fonte
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
    }

}
Mat0
fonte
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
}
Faris Muhammed
fonte