Jak wyłączyć gest machnięcia wstecz w UINavigationController na iOS 7

326

W iOS 7 Apple dodał nowe domyślne zachowanie nawigacyjne. Możesz przesunąć palcem od lewej krawędzi ekranu, aby wrócić do stosu nawigacji. Ale w mojej aplikacji takie zachowanie jest sprzeczne z moim niestandardowym lewym menu. Czy więc można wyłączyć ten nowy gest w UINavigationController?

ArtFeel
źródło
2
Dowiedziałem się również, że jeśli ustawisz navigationItem.hidesBackButton = true, ten gest również zostanie wyłączony. W moim przypadku zaimplementowałem niestandardowy przycisk Wstecz i dodaję jakoleftBarButtonItem
Umair

Odpowiedzi:

586

Znalazłem rozwiązanie:

Cel C:

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

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

ArtFeel
źródło
29
Oczywiście musisz sprawdzić dostępność nowych metod, jeśli wspierasz stare wersje iOS.
ArtFeel,
2
Czy istnieje sposób, aby wyłączyć go na miksturę widoku?
Marc
11
Możesz enable / disablerozpoznać na viewDidAppear:/ viewDidDisappear. Lub możesz zaimplementować UIGestureRecognizerDelegateprotokół przy użyciu bardziej złożonej logiki i ustawić go jako recognizer.delegatewłaściwość.
ArtFeel,
26
Na systemów iOS 8, ustawiając self.navigationController.interactivePopGestureRecognizer.enabledwłaściwość nie działa w następujących metod widok na: viewDidLoad, viewWillAppear, viewDidAppear, viewDidDisappear, ale prace w metodzie viewWillDisappear. Na iOS7 działa we wszystkich wyżej wymienionych metodach. Więc spróbuj użyć go w innych metodach podczas pracy z viewController, potwierdzam, że działa dla mnie na iOS8, kiedy kliknę jakiś przycisk w widoku.
Sihad Begovic
8
Potwierdza, że ​​to nie zadziała w iOS8 w widoku Widmo załadowania i wyświetlenie Widoku, umieszczenie go w widoku wyświetlił się w podglądzie pokazów
tonytastic
47

Dowiedziałem się, że ustawienie wyłączonego gestu nie zawsze działa. To działa, ale dla mnie zadziałało dopiero po tym, jak raz użyłem backgesture. Za drugim razem nie spowodowałoby to opóźnienia.

Naprawiono dla mnie delegowanie gestu i zaimplementowanie metody shouldbegin w celu zwrócenia NIE:

- (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
źródło
1
Dzięki! Jest to wymagane do pełnego wyłączenia przesunięcia wstecz. Wciąż istnieje w iOS 8 i pachnie jak błąd Apple.
Eric Chen
Dziękuję, wydaje się, że jedyna rzecz, która zadziałała.
Ben
Nie wiem dlaczego, ale kontroler widoku w mojej aplikacji z jakiegoś nieznanego powodu powodował awarię tego gestu cofania .. uratowało mnie to od znalezienia go, ponieważ nie potrzebowałem tego gestu wstecz, więc wyłączyłem używanie tego kodu .. +1
Ahsan Ebrahim
1
@AhsanEbrahim, gdy rozpoczyna się gest wstecz, viewWillAppearjest wywoływany w widoku za bieżącym widokiem. Może to spowodować spustoszenie w logice kodu, ponieważ bieżący widok jest nadal aktywny. Może to być przyczyną twojego wypadku.
phatmann
Czy enabledpotrzebne są wiersze tak / nie? Wracasz NOz gestureRecognizerShouldBegin, czy to nie wystarczy?
ToolmakerSteve,
30

Wystarczy usunąć rozpoznawanie gestów z NavigationController. Pracuj w iOS 8.

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    [self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];
Vladimir Samoylov
źródło
jedyne rozwiązanie, które faktycznie działa w iOS 8 i 9
Kappe
7
Działa również w systemie iOS 10, powinna to być zaakceptowana odpowiedź. Nawiasem mówiąc, jeśli chcesz go ponownie włączyć, zrób [self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]gdzieś.
ooops
22

Począwszy od iOS 8, zaakceptowana odpowiedź już nie działa. Musiałem zatrzymać przesuwanie, aby odrzucić gest na głównym ekranie gry, więc zaimplementowałem to:

- (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
źródło
2
Podczas gdy działa to z iOS8, pojawia się ostrzeżenie w wierszu * .delegate = self; stwierdzając: Przypisywanie do identyfikatora <UIGestureRecognizerDelegate> od niekompatybilnego typu „ViewController * const __strong”
David Douglas
2
Począwszy od iOS8, zaakceptowana odpowiedź nadal działa zgodnie z oczekiwaniami. Prawdopodobnie robisz coś innego źle…
Alexandre G.
Udało się, aby działało to częściowo, wywołując zaakceptowaną odpowiedź w viewWillLayoutSubviews. Jednak przesunięcie spowodowało, że strona ponownie wywołała „viewDidLoad”, więc wróciłem do mojej powyższej odpowiedzi
Charlie Seligman,
@DavidDouglas: być może mógłbyś usunąć ostrzeżenie za pomocą tego kodu: __weak __typeof (self) theSafeSelf = self? Następnie ustaw delegata na SafeSelf.
lifjoy
1
@DavidDouglas: Musisz dodać <UIGestureRecognizerDelegate> do interfejsu, aby pozbyć się tego ostrzeżenia
primehalo
20

Ulepszyłem nieco odpowiedź Twana, ponieważ:

  1. Twój kontroler widoku może być ustawiony jako delegat do innych rozpoznających gesty
  2. ustawienie delegata na nilprowadzi do problemów z zawieszaniem się, gdy wrócisz do kontrolera widoku głównego i wykonasz gest machnięcia przed nawigacją w innym miejscu.

Poniższy przykład zakłada 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;
}
Jacek
źródło
+1 „ustawienie delegata na zero prowadzi do problemów z zawieszaniem się, gdy wracasz do kontrolera widoku głównego i wykonujesz gest machnięcia przed nawigacją w innym miejscu”.
albertamg
10

Proszę ustawić to w 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
źródło
9

Dla Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false
iPhone 7
źródło
12
To działa, ale sugeruję użycie opcjonalnego łączenia zamiast wymuszania rozpakowywania. np. self.navigationController? .interactivePopGestureRecognizer? .isEnabled = false
Womble
5

działa dla mnie w iOS 10 i późniejszych:

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

}

nie działa na metodzie viewDidLoad ().

Logika
źródło
5

EDYTOWAĆ

Jeśli chcesz zarządzać funkcją przesunięcia wstecz dla określonych kontrolerów nawigacji, rozważ użycie SwipeBack .

Dzięki temu możesz ustawić navigationController.swipeBackEnabled = NO .

Na przykład:

#import <SwipeBack/SwipeBack.h>

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

Można go zainstalować za pośrednictwem CocoaPods .

pod 'SwipeBack', '~> 1.0'

Przepraszam za brak wyjaśnień.

devxoul
źródło
6
Promując projekt, w który jesteś zaangażowany, musisz ujawnić swoje powiązanie z nim.
2
Co więcej, jedynym celem twojego projektu jest ręczne włączenie gestu przeciągnięcia, gdy domyślny systemowy nie działa, podczas gdy pytanie dotyczy sposobu wyłączenia tego gestu dla całego systemu, więc nawet jeśli ustawisz self.navigationController.swipeBackEnabled = NO, jestem pewien, że spowoduje to wyłączenie twojego gest przesunięcia biblioteki do tyłu, ale systemowy nadal będzie włączony.
1
Przepraszam za moją krótką odpowiedź, właśnie zredagowałem swoją odpowiedź z dodatkowymi informacjami: „przydatne dla określonych kontrolerów nawigacyjnych”. Dzięki!
devxoul
Wygląda na to, że używa swizzle, co nie jest już dozwolone. iOS8?
Matt
1
@devxoul Przepraszam! Myślałem, że jakiś czas temu przeczytałem, mówiąc, że zamiatanie nie jest już dozwolone. Nie mogę jednak znaleźć niczego, co by to mówiło. Chyba się mylę.
Matt
4

Moja metoda Rozpoznawanie gestów, aby rządzić nimi wszystkimi:

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

Ważne: nie resetuj delegata w dowolnym miejscu stosu nawigacji: navigationController!.interactivePopGestureRecognizer!.delegate = nil

SoftDesigner
źródło
3

Tak działa Swift 3

pracuje dla mnie

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
Tayo119
źródło
3

Wszystkie te rozwiązania manipulują rozpoznawaniem gestów Apple w sposób, którego nie zalecają. Przyjaciel powiedział mi właśnie, że istnieje lepsze rozwiązanie:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

gdzie myPanGestureRecognizer to rozpoznawanie gestów, którego używasz np. do wyświetlania menu. W ten sposób rozpoznawanie gestów Apple nie włącza się ponownie po naciśnięciu nowego kontrolera nawigacyjnego i nie trzeba polegać na hackujących opóźnieniach, które mogą zadziałać zbyt wcześnie, jeśli telefon zostanie uśpiony lub obciążony.

Zostawię to tutaj, ponieważ wiem, że nie zapamiętam tego następnym razem, gdy będę go potrzebować, a następnie znajdę rozwiązanie tego problemu tutaj.

wszechstronność
źródło
3

swift 5, swift 4.2 może korzystać z kodu poniżej.

// disable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// enable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
Zgpeace
źródło
2

Żadna z podanych odpowiedzi nie pomogła mi rozwiązać problemu. Zamieszczam tutaj moją odpowiedź; może być komuś pomocny

Zadeklaruj private var popGesture: UIGestureRecognizer?jako zmienną globalną w kontroler widoku. Następnie zaimplementować kod w viewDidAppear i viewWillDisappear metod

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

To spowoduje wyłączenie machnięcia z powrotem w iOS v8.x roku

Augustine PA
źródło
Próbuję sobie wyobrazić, w jakich okolicznościach to zadziałałoby, ale Jack nie. Mówisz, że wypróbowałeś wszystkie pozostałe odpowiedzi: co poszło nie tak, gdy wypróbowałeś Jacka?
ToolmakerSteve,
Z drugiej strony wydaje się to prostsze niż Jacka, więc może to nie jest ważne. Zdecydowałem, że mi się to podoba, ponieważ nie muszę deklarować mojej klasy jako delegata ani manipulować interactivePopGestureRecognizer.delegate.
ToolmakerSteve,
BTW, kod można uprościć. Usuń if( .. respondsToSelector ... Następny wiersz ustawia popGesture na rozpoznawanie lub na zero. Następnie użyć tej wartości: if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture ).
ToolmakerSteve,
2

Działa to w viewDidLoad:systemie 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;
  });

Wiele problemów można rozwiązać za pomocą dobrych starych dispatch_after .

Chociaż należy pamiętać, że to rozwiązanie jest potencjalnie niebezpieczne, użyj własnego uzasadnienia.

Aktualizacja

W przypadku iOS 8.1 czas opóźnienia powinien wynosić 0,5 sekundy

W iOS 9.3 nie jest już potrzebne żadne opóźnienie, działa po prostu umieszczając to w viewDidLoad:
(TBD, jeśli działa na iOS 9.0-9.3)

navigationController?.interactivePopGestureRecognizer?.enabled = false
Dannie P.
źródło
O ile nie wiesz, kiedy rozpoznawanie gestów jest zainstalowane w widoku, odczekanie dowolnej ilości czasu, aby je wyłączyć, może działać lub nie.
kalperin
@kalperin nie gwarantuje, że zadziała, choć czasami jest to bardzo przydatne rozwiązanie. Użyj własnego rozumowania.
Dannie P.
Działa dla mnie, posiadając wersję większą niż iOS 8.1 :)
iChirag
viewDidLoadplus opóźnienie jest ryzykowną praktyką programistyczną. Zły nawyk na start. Co się stanie, jeśli użytkownik rozpocznie przeciąganie, zanim rozpocznie się opóźnione połączenie? Nie ma bezpiecznego czasu, który byłby wystarczająco długi, ale nie za długi. Dlatego inne odpowiedzi, opublikowane na długo przed twoimi, sugerują umieszczenie kodu viewDidAppear. To gwarantuje, że wszystko jest zainstalowane. Nie wymyślaj arbitralnych opóźnień; używaj sekwencji połączeń Apple zgodnie z przeznaczeniem.
ToolmakerSteve,
1
@ iChirag true. Zauważyłem, że dla wersji 8.1 potrzebujesz 0,5-sekundowego opóźnienia
Dannie P
1

W przypadku Swift 4 działa to:

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
źródło
Nie należy zastępować delegata interaktywnego gestu pop, ponieważ spowoduje to nieudokumentowane zachowanie
Josh Bernfeld
Myślę, że tak naprawdę nie zastępuje on delegata, a jedynie modyfikuje zmienną logiczną, którą podali właśnie w tym celu, więc nie będzie problemu
Lok SN
0

To działało dla mnie dla większości kontrolerów widoku.

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

Nie działało to w przypadku niektórych kontrolerów widoku, takich jak UIPageViewController. Na UIPageViewController pagecontentviewcontroller poniższy kod działał dla mnie.

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
}

Na UIGestureRecognizerDelegate,

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
   if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
      return false
}
      return true
}
Faris Muhammed
źródło