Come disabilitare il gesto di back swipe in UINavigationController su iOS 7


326

In iOS 7 Apple ha aggiunto un nuovo comportamento di navigazione predefinito. Puoi scorrere dal bordo sinistro dello schermo per tornare allo stack di navigazione. Ma nella mia app, questo comportamento è in conflitto con il mio menu a sinistra personalizzato. Quindi, è possibile disabilitare questo nuovo gesto in UINavigationController?



2
Ho anche scoperto che se impostato navigationItem.hidesBackButton = true, anche questo gesto viene disabilitato. Nel mio caso ho implementato un pulsante Indietro personalizzato e l'aggiunta comeleftBarButtonItem
Umair

Risposte:


586

Ho trovato una soluzione:

Objective-C:

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

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


29
Naturalmente, è necessario verificare la disponibilità di nuovi metodi se si supportano le versioni precedenti di iOS.
ArtFeel,

2
C'è un modo per disabilitarlo per una pozione della vista?
Marc,

11
Puoi enable / disablericonoscere su viewDidAppear:/ viewDidDisappear. In alternativa, è possibile implementare il UIGestureRecognizerDelegateprotocollo con la logica più complessa e impostarlo come recognizer.delegateproprietà.
ArtFeel

26
Su iOS 8, l'impostazione self.navigationController.interactivePopGestureRecognizer.enableddi proprietà non funziona in seguito metodi di visualizzazione: viewDidLoad, viewWillAppear, viewDidAppear, viewDidDisappear, ma opere nel metodo viewWillDisappear. Su iOS7 funziona con tutti i metodi sopra menzionati. Quindi prova a usarlo in qualsiasi altro metodo mentre lavori su viewController, confermo che funziona su iOS8 quando faccio clic su un pulsante all'interno della vista.
Sihad Begovic,

8
Posso confermare che questo non funzionerà in iOS8 in viewDidLoad e viewWillAppear, mettendolo in viewwilllayoutgubviews ha fatto il trucco
tonytastic il

47

Ho scoperto che l'impostazione del gesto su Disabilitato non funziona sempre. Funziona, ma per me ha funzionato solo dopo aver usato una volta il backgesture. La seconda volta non si innescherebbe il backgesture.

La correzione per me era quella di delegare il gesto e implementare il metodo shouldbegin per restituire 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
Grazie! Questo è necessario per disabilitare completamente il back-swipe. Esiste ancora in iOS 8 e ha l'odore di un bug di Apple.
Eric Chen,

Grazie, sembra essere l'unica cosa che ha funzionato.
Ben

Non so perché, ma un controller di visualizzazione nella mia app per qualche ragione sconosciuta si è schiantato su questo gesto di schiena .. questo mi ha salvato dal trovarlo perché non avevo bisogno di questo gesto di schiena e quindi ho disabilitato usando questo codice .. +1
Ahsan Ebrahim

1
@AhsanEbrahim, quando inizia il gesto della schiena, viewWillAppearviene chiamato nella vista dietro la vista corrente. Ciò può causare il caos nella logica del codice poiché la vista corrente è ancora attiva. Potrebbe essere la causa del tuo incidente.
Phatmann,

Sono enablednecessarie le linee sì / no? Si ritorna NOda gestureRecognizerShouldBegin, non è che è sufficiente?
ToolmakerSteve

30

Basta rimuovere il riconoscimento gesti da NavigationController. Lavora in iOS 8.

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

l'unica soluzione che funziona effettivamente in iOS 8 e 9
Kappe,

7
Funziona anche con iOS 10, questa dovrebbe essere la risposta accettata. A proposito, se vuoi riattivarlo, fallo [self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]da qualche parte.
ooops,

22

A partire da iOS 8 la risposta accettata non funziona più. Avevo bisogno di fermare lo swipping per eliminare il gesto sulla mia schermata di gioco principale, quindi ho implementato questo:

- (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
Mentre funziona con iOS8 ricevo un avviso sulla linea * .delegate = self; affermando: Assegnare all'ID <UIGestureRecognizerDelegate> 'dal tipo incompatibile' ViewController * const __strong '
David Douglas

2
A partire da iOS8, la risposta accettata funziona ancora come previsto. Probabilmente stai sbagliando qualcos'altro ...
Alexandre G,

Gestito per renderlo semi funzionante chiamando la risposta accettata in viewWillLayoutSubviews. Tuttavia, lo scorrimento ha fatto sì che la pagina chiamasse di nuovo "viewDidLoad", così è tornato alla mia risposta sopra
Charlie Seligman,

@DavidDouglas: forse potresti eliminare l'avviso con questo codice: __weak __typeof (self) theSafeSelf = self? Quindi impostare il delegato su theSafeSelf.
lifjoy

1
@DavidDouglas: è necessario aggiungere <UIGestureRecognizerDelegate> all'interfaccia per sbarazzarsi di
quell'avvertimento

20

Ho perfezionato un po 'la risposta di Twan, perché:

  1. il controller della vista può essere impostato come delegato per altri riconoscitori di gesti
  2. l'impostazione del delegato su nilporta a problemi di sospensione quando torni al controller della vista principale e fai un gesto di scorrimento prima di navigare altrove.

L'esempio seguente presuppone 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 "L'impostazione del delegato su zero comporta problemi di sospensione quando torni al controller della vista principale e fai un gesto di scorrimento prima di navigare altrove."
albertamg,

10

Si prega di impostare questo nel 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;
}

9

Per Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false

12
Funziona, anche se suggerirei di utilizzare il concatenamento opzionale invece di forzare lo scartamento. ad es. self.navigationController? .interactivePopGestureRecognizer? .isEnabled = false
Womble

5

funziona per me in iOS 10 e versioni successive:

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

}

non funziona con il metodo viewDidLoad ().


5

MODIFICARE

Se si desidera gestire la funzione di scorrimento indietro per specifici controller di navigazione, considerare l'utilizzo di SwipeBack .

Con questo, puoi impostare navigationController.swipeBackEnabled = NO.

Per esempio:

#import <SwipeBack/SwipeBack.h>

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

Può essere installato tramite CocoaPods .

pod 'SwipeBack', '~> 1.0'

Mi scuso per la mancanza di spiegazioni.


6
Quando promuovi un progetto in cui sei coinvolto, devi rivelare la tua affiliazione con esso.

2
Inoltre, l'unico scopo del tuo progetto è abilitare manualmente il gesto di scorrimento quando il sistema predefinito non funziona, mentre la domanda chiede come disabilitare quel gesto a livello di sistema, quindi anche se imposti self.navigationController.swipeBackEnabled = NOsono abbastanza sicuro che questo disabiliterà solo il tuo gesto di scorrimento indietro della libreria ma quello del sistema sarà ancora abilitato.

1
Ci scusiamo per la mia breve risposta, ho appena modificato la mia risposta con ulteriori informazioni: "utile per specifici controller di navigazione". Grazie!
Devxoul,

Sembra usare swizzle che non è più consentito. iOS 8?
Matt,

1
@devxoul mi dispiace! Pensavo di aver letto qualcosa qualche tempo fa che diceva che non era più permesso sfrigolare. Tuttavia, non riesco a trovare nulla che dica questo. Immagino di sbagliarmi.
Matt,

4

Il mio metodo. Un riconoscimento gestuale per dominarli tutti:

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: non reimpostare il delegato in nessun punto dello stack di navigazione: navigationController!.interactivePopGestureRecognizer!.delegate = nil


3

Questo è il modo in Swift 3

per me va bene

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

3

Tutte queste soluzioni manipolano il riconoscimento dei gesti di Apple in un modo che non raccomandano. Un amico mi ha appena detto che esiste una soluzione migliore:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

dove myPanGestureRecognizer è il riconoscimento dei gesti che stai utilizzando, ad esempio per mostrare il tuo menu. In questo modo, il riconoscimento dei gesti di Apple non viene riacceso da loro quando si preme un nuovo controller di navigazione e non è necessario fare affidamento su ritardi anomali che potrebbero attivarsi troppo presto se il telefono viene messo in modalità di sospensione o sotto carico pesante.

Lasciando questo qui perché so che non lo ricorderò la prossima volta che ne avrò bisogno, e quindi avrò la soluzione al problema qui.


3

swift 5, swift 4.2 può usare il codice in basso.

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

2

Nessuna delle risposte fornite mi ha aiutato a risolvere il problema. Pubblicando la mia risposta qui; può essere utile per qualcuno

Dichiara private var popGesture: UIGestureRecognizer?come variabile globale nel tuo viewcontroller. Quindi implementare il codice nei metodi 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!)
    }
}

Ciò disabiliterà il passaggio indietro in iOS v8.x in poi


Sto cercando di immaginare in quali circostanze questo funzionerebbe, ma Jack non lo farebbe. Dici di aver provato tutte le altre risposte: cosa è andato storto quando hai provato Jack?
ToolmakerSteve

D'altra parte, questo sembra più semplice di quello di Jack, quindi forse non è importante. Deciso che mi piace, perché non devo dichiarare la mia classe come delegato, né manipolare interactivePopGestureRecognizer.delegate.
ToolmakerSteve

A proposito, il codice può essere semplificato. Rimuovi if( .. respondsToSelector ... La riga successiva imposta popGesture su un riconoscitore o su zero. Quindi utilizzare il suo valore: if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture ).
ToolmakerSteve

2

Funziona con 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;
  });

Molti problemi potrebbero essere risolti con l'aiuto del buon vecchio dispatch_after.

Sebbene si noti che questa soluzione non è potenzialmente sicura, utilizzare il proprio ragionamento.

Aggiornare

Per iOS 8.1 il tempo di ritardo dovrebbe essere di 0,5 secondi

Su iOS 9.3 non è più necessario alcun ritardo, funziona semplicemente inserendo questo nel tuo viewDidLoad:
(TBD se funziona su iOS 9.0-9.3)

navigationController?.interactivePopGestureRecognizer?.enabled = false

A meno che non si sappia quando il rilevatore di gesti è installato sulla vista, l'attesa di un intervallo di tempo arbitrario per disabilitarlo potrebbe non funzionare.
Kalperin,

@kalperin non è garantito per funzionare, anche se a volte è una soluzione molto utile. Usa il tuo ragionamento.
Dannie P,

Funziona per me con una versione superiore a iOS 8.1 :)
iChirag,

viewDidLoadinoltre il ritardo è una pratica programmazione rischiosa. Una cattiva abitudine per iniziare. Cosa succede se l'utente inizia a scorrere prima che inizi la chiamata in ritardo? Non c'è tempo sicuro che è garantito per essere abbastanza lungo ma non troppo lungo. Ecco perché altre risposte, pubblicate molto prima delle tue, suggeriscono di inserire il codice viewDidAppear. Ciò garantisce che tutto sia installato. Non inventare ritardi arbitrari; utilizzare la sequenza di chiamate di Apple come previsto.
ToolmakerSteve

1
@iChirag true. Ho notato che per 8.1 hai bisogno di 0,5 secondi di ritardo
Dannie P

1

Per Swift 4 funziona:

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
    }

}

Non dovresti ignorare il delegato del gesto pop interattivo in quanto causerà un comportamento non documentato
Josh Bernfeld

Penso che non stia davvero scavalcando il delegato, ma semplicemente modificando la variabile booleana che hanno fornito proprio per questo scopo, quindi non sarà un problema
Lok SN,

0

Ha funzionato per me per la maggior parte dei viewcontroller.

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

Non funzionava per alcuni viewcontroller come UIPageViewController. Su UIPageViewController il pagecontentviewcontroller sotto il codice ha funzionato per me.

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
}

Su UIGestureRecognizerDelegate,

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
   if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
      return false
}
      return true
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.