Deaktivieren der Back Swipe-Geste in UINavigationController unter iOS 7

326

In iOS 7 hat Apple ein neues Standardnavigationsverhalten hinzugefügt. Sie können vom linken Bildschirmrand wischen, um zum Navigationsstapel zurückzukehren. In meiner App widerspricht dieses Verhalten jedoch meinem benutzerdefinierten linken Menü. Ist es also möglich, diese neue Geste in UINavigationController zu deaktivieren?

ArtFeel
quelle
2
Ich habe auch herausgefunden, dass navigationItem.hidesBackButton = truediese Geste auch deaktiviert wird, wenn Sie sie einstellen . In meinem Fall habe ich einen benutzerdefinierten Zurück-Button implementiert und alsleftBarButtonItem
Umair

Antworten:

586

Ich habe eine Lösung gefunden:

Ziel c:

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

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

ArtFeel
quelle
29
Natürlich müssen Sie die Verfügbarkeit neuer Methoden überprüfen, wenn Sie alte Versionen von iOS unterstützen.
ArtFeel
2
Gibt es eine Möglichkeit, es für einen Trank der Ansicht zu deaktivieren?
Marc
11
Sie können enable / disableauf viewDidAppear:/ erkennen viewDidDisappear. Sie können das UIGestureRecognizerDelegateProtokoll auch mit Ihrer komplexeren Logik implementieren und als recognizer.delegateEigenschaft festlegen .
ArtFeel
26
Auf iOS8, Einstellung self.navigationController.interactivePopGestureRecognizer.enabledEigenschaft funktioniert nicht nach Ansicht der Methoden in der folgenden: viewDidLoad, viewWillAppear, viewDidAppear, viewDidDisappear, funktioniert aber in der Methode viewWillDisappear. Unter iOS7 funktioniert es mit allen oben genannten Methoden. Versuchen Sie also, es in anderen Methoden zu verwenden, während Sie am viewController arbeiten. Ich bestätige, dass es unter iOS8 für mich funktioniert, wenn ich auf eine Schaltfläche in der Ansicht klicke.
Sihad Begovic
8
Kann bestätigen, dass dies in iOS8 in viewDidLoad und viewWillAppear nicht funktioniert. Das Einfügen in viewwilllayoutgubviews hat den Trick
getan
47

Ich habe herausgefunden, dass es nicht immer funktioniert, die Geste auf deaktiviert zu setzen. Es funktioniert, aber für mich erst, nachdem ich die Backgesture einmal benutzt habe. Zum zweiten Mal würde es die Backgesture nicht auslösen.

Fix für mich war, die Geste zu delegieren und die Shouldbegin-Methode zu implementieren, um NO zurückzugeben:

- (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
quelle
1
Vielen Dank! Dies ist erforderlich, um das Zurückwischen vollständig zu deaktivieren. Es existiert immer noch in iOS 8 und riecht nach einem Apple-Fehler.
Eric Chen
Danke, scheint das einzige zu sein, was funktioniert hat.
Ben
Ich weiß nicht warum, aber ein View Controller in meiner App stürzte aus einem unbekannten Grund bei dieser Rückgeste ab. Dies ersparte mir das Auffinden, da ich diese Rückgeste nicht benötigte und daher die Verwendung dieses Codes deaktivierte. +1
Ahsan Ebrahim
1
@AhsanEbrahim wird beim Starten der Rückgeste viewWillAppearin der Ansicht hinter der aktuellen Ansicht aufgerufen. Dies kann zu Chaos in der Codelogik führen, da die aktuelle Ansicht noch aktiv ist. Könnte die Ursache für Ihren Absturz sein.
Phatmann
Werden die enabledJa / Nein-Zeilen benötigt? Sie kehren NOaus gestureRecognizerShouldBegin, ist das nicht ausreichend?
ToolmakerSteve
30

Entfernen Sie einfach die Gestenerkennung aus NavigationController. Arbeiten Sie in iOS 8.

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    [self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];
Vladimir Samoylov
quelle
Die einzige Lösung, die tatsächlich in iOS 8 und 9 funktioniert
Kappe
7
Funktioniert auch in iOS 10, dies sollte die akzeptierte Antwort sein. Übrigens, wenn Sie es wieder aktivieren möchten, tun Sie es [self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]irgendwo.
ooops
22

Ab iOS 8 funktioniert die akzeptierte Antwort nicht mehr. Ich musste das Wischen stoppen, um die Geste auf meinem Hauptspielbildschirm zu verwerfen.

- (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
quelle
2
Während dies mit iOS8 funktioniert, erhalte ich eine Warnung in der Zeile * .delegate = self; Angabe: Zuweisen zur ID <UIGestureRecognizerDelegate> 'vom inkompatiblen Typ' ViewController * const __strong '
David Douglas
2
Ab iOS8 funktioniert die akzeptierte Antwort weiterhin wie erwartet. Sie machen wahrscheinlich etwas anderes falsch ..
Alexandre G
Es gelang, es halb funktionsfähig zu machen, indem die akzeptierte Antwort in viewWillLayoutSubviews aufgerufen wurde. Das Wischen führte jedoch dazu, dass die Seite erneut 'viewDidLoad' aufrief, sodass ich zu meiner obigen Antwort zurückkehrte
Charlie Seligman,
@ DavidDouglas: Vielleicht könnten Sie die Warnung mit diesem Code beseitigen: __weak __typeof (self) theSafeSelf = self? Stellen Sie dann den Delegaten auf theSafeSelf ein.
Lebensfreude
1
@ DavidDouglas: Sie müssen <UIGestureRecognizerDelegate> zur Schnittstelle hinzufügen, um diese Warnung zu
entfernen
20

Ich habe Twans Antwort ein wenig verfeinert, weil:

  1. Ihr Ansichts-Controller kann als Delegierter für andere Gestenerkenner festgelegt werden
  2. Das Festlegen des Delegaten auf nilführt zu Problemen beim Aufhängen, wenn Sie zum Root-View-Controller zurückkehren und eine Wischgeste ausführen, bevor Sie an eine andere Stelle navigieren.

Das folgende Beispiel setzt iOS 7 voraus:

{
    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
quelle
+1 "Das Setzen des Delegaten auf Null führt zu Problemen beim Aufhängen, wenn Sie zum Root-View-Controller zurückkehren und eine Wischgeste ausführen, bevor Sie an eine andere Stelle navigieren."
Albertamg
10

Bitte setzen Sie dies in 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
quelle
9

Für Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false
iPhone 7
quelle
12
Dies funktioniert, obwohl ich vorschlagen würde, eine optionale Verkettung zu verwenden, anstatt das Auspacken zu erzwingen. zB self.navigationController? .interactivePopGestureRecognizer? .isEnabled = false
Womble
5

es funktioniert für mich in ios 10 und höher:

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

}

Bei der viewDidLoad () -Methode funktioniert dies nicht.

Logik
quelle
5

BEARBEITEN

Wenn Sie die Swipe-Back-Funktion für bestimmte Navigationscontroller verwalten möchten, sollten Sie SwipeBack verwenden .

Damit können Sie einstellen navigationController.swipeBackEnabled = NO.

Zum Beispiel:

#import <SwipeBack/SwipeBack.h>

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

Es kann über CocoaPods installiert werden .

pod 'SwipeBack', '~> 1.0'

Ich entschuldige mich für mangelnde Erklärung.

devxoul
quelle
6
Wenn Sie für ein Projekt werben, an dem Sie beteiligt sind, müssen Sie Ihre Zugehörigkeit zu diesem Projekt offenlegen.
2
Darüber hinaus besteht der einzige Zweck Ihres Projekts darin, die Wischgeste manuell zu aktivieren, wenn das Standardsystem nicht funktioniert, während in der Frage gefragt wird, wie diese systemweite Geste deaktiviert werden soll. Selbst wenn Sie sie festlegen, self.navigationController.swipeBackEnabled = NObin ich mir ziemlich sicher, dass dies nur Ihre deaktiviert Die Swipe-Back-Geste der Bibliothek, die des Systems bleibt jedoch aktiviert.
1
Entschuldigung für meine kurze Antwort, ich habe meine Antwort gerade mit zusätzlichen Informationen bearbeitet: "nützlich für bestimmte Navigationssteuerungen". Vielen Dank!
Devxoul
Es scheint Swizzle zu verwenden, was nicht mehr erlaubt ist. iOS8?
Matt
1
@devxoul Es tut mir leid! Ich dachte, ich hätte vor einiger Zeit etwas gelesen, das besagt, dass das Zischen nicht mehr erlaubt ist. Ich kann jedoch nichts finden, was dies sagt. Ich glaube, ich liege falsch.
Matt
4

Meine Methode. Ein Gestenerkenner, der sie alle beherrscht:

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)
    }
}

Wichtig: Setzen Sie den Delegaten nirgendwo im Navigationsstapel zurück: navigationController!.interactivePopGestureRecognizer!.delegate = nil

SoftDesigner
quelle
3

Dies ist der Weg auf Swift 3

funktioniert bei mir

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

Alle diese Lösungen manipulieren Apples Gestenerkennung auf eine Weise, die sie nicht empfehlen. Mir wurde gerade von einem Freund gesagt, dass es eine bessere Lösung gibt:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

Dabei ist myPanGestureRecognizer der Gestenerkenner, mit dem Sie z. B. Ihr Menü anzeigen. Auf diese Weise wird Apples Gestenerkennung von ihnen nicht wieder eingeschaltet, wenn Sie einen neuen Navigationscontroller drücken, und Sie müssen sich nicht auf hackige Verzögerungen verlassen, die möglicherweise zu früh ausgelöst werden, wenn Ihr Telefon in den Ruhezustand versetzt oder stark belastet wird.

Lassen Sie dies hier, weil ich weiß, dass ich mich beim nächsten Mal nicht mehr daran erinnern werde, und dann habe ich hier die Lösung für das Problem.

uliwitness
quelle
3

swift 5, swift 4.2 kann den folgenden Code verwenden.

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

Keine der gegebenen Antworten half mir, das Problem zu lösen. Poste meine Antwort hier; kann für jemanden hilfreich sein

Deklarieren Sie private var popGesture: UIGestureRecognizer?in Ihrem Viewcontroller als globale Variable. Implementieren Sie dann den Code in den Methoden viewDidAppear und 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!)
    }
}

Dadurch wird das Zurückwischen ab iOS v8.x deaktiviert

Augustine PA
quelle
Ich versuche mir vorzustellen, unter welchen Umständen dies funktionieren würde, aber Jacks nicht. Sie sagen, Sie haben alle anderen Antworten ausprobiert: Was ist schief gelaufen, als Sie Jacks ausprobiert haben?
ToolmakerSteve
Auf der anderen Seite scheint dies einfacher zu sein als das von Jack, also ist es vielleicht nicht wichtig. Ich habe beschlossen, dass mir das gefällt, weil ich meine Klasse nicht als Delegierten deklarieren oder manipulieren muss interactivePopGestureRecognizer.delegate.
ToolmakerSteve
Übrigens kann Code vereinfacht werden. Entfernen if( .. respondsToSelector ... In der nächsten Zeile wird popGesture auf einen Erkenner oder auf Null gesetzt. Verwenden Sie dann den Wert : if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture ).
ToolmakerSteve
2

Dies funktioniert unter 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;
  });

Viele der Probleme konnten mit Hilfe des guten alten gelöst werden dispatch_after .

Bitte beachten Sie, dass diese Lösung möglicherweise unsicher ist. Verwenden Sie jedoch Ihre eigenen Überlegungen.

Aktualisieren

Für iOS 8.1 sollte die Verzögerungszeit 0,5 Sekunden betragen

Unter iOS 9.3 ist keine Verzögerung mehr erforderlich. Sie können dies einfach in Folgendes einfügen viewDidLoad:
(TBD, wenn unter iOS 9.0-9.3 funktioniert)

navigationController?.interactivePopGestureRecognizer?.enabled = false
Dannie P.
quelle
Wenn Sie nicht wissen, wann der Gestenerkenner in der Ansicht installiert ist, kann es sein, dass eine beliebige Zeitspanne zum Deaktivieren funktioniert oder nicht.
Kalperin
@kalperin es ist nicht garantiert zu funktionieren, obwohl es manchmal eine sehr praktische Lösung ist. Verwenden Sie Ihre eigenen Überlegungen.
Dannie P
Es funktioniert für mich mit einer Version größer als iOS 8.1 :)
iChirag
viewDidLoadPlus Verzögerung ist eine riskante Programmierpraxis. Eine schlechte Angewohnheit zu beginnen. Was passiert, wenn der Benutzer den Wischvorgang startet, bevor Ihr verspäteter Anruf beginnt? Es gibt keine sichere Zeit, die garantiert lang genug und doch nicht zu lang ist. Aus diesem Grund schlagen andere Antworten, die lange vor Ihren veröffentlicht wurden, vor, den Code einzufügen viewDidAppear. Das stellt sicher, dass alles installiert ist. Erfinde keine willkürlichen Verzögerungen. Verwenden Sie die beabsichtigte Anruffolge von Apple.
ToolmakerSteve
1
@iChirag wahr. Ich habe festgestellt, dass Sie für 8.1 0,5 Sekunden Verzögerung benötigen
Dannie P
1

Für Swift 4 funktioniert dies:

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
quelle
Sie sollten den interaktiven Pop-Gesten-Delegaten nicht überschreiben, da dies zu undokumentiertem Verhalten führt
Josh Bernfeld,
Ich denke, es überschreibt den Delegaten nicht wirklich, sondern ändert nur die Boolesche Variable, die sie für diesen Zweck bereitgestellt haben, damit es kein Problem darstellt
Lok SN
0

Bei den meisten Viewcontrollern hat es bei mir funktioniert.

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

Bei einigen Viewcontrollern wie UIPageViewController funktionierte dies nicht. Auf der Seite von UIPageViewController funktionierte der Codecontentviewcontroller unten für mich.

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
}

In UIGestureRecognizerDelegate

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