Как отключить жест смахивания назад в UINavigationController на iOS 7

326

В iOS 7 Apple добавила новое поведение навигации по умолчанию. Вы можете провести пальцем от левого края экрана, чтобы вернуться в стек навигации. Но в моем приложении это поведение противоречит моему левому меню. Итак, возможно ли отключить этот новый жест в UINavigationController?

ArtFeel
источник
2
Я также узнал, что если вы установите navigationItem.hidesBackButton = true, этот жест также отключается. В моем случае я реализовал пользовательскую кнопку возврата и добавил какleftBarButtonItem
Umair

Ответы:

586

Я нашел решение:

Objective-C:

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

Свифт 3+:
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

ArtFeel
источник
29
Конечно, вам нужно проверить наличие новых методов, если вы поддерживаете старые версии iOS.
ArtFeel
2
Есть ли способ отключить его для зелья зрения?
Marc
11
Вы можете enable / disableузнать на viewDidAppear:/ viewDidDisappear. Или вы можете реализовать UIGestureRecognizerDelegateпротокол с более сложной логикой и установить его как recognizer.delegateсвойство.
ArtFeel
26
На iOS8, установка self.navigationController.interactivePopGestureRecognizer.enabledсвойства не работает в следующих методов зрения в: viewDidLoad, viewWillAppear, viewDidAppear, viewDidDisappear, но работает в методе viewWillDisappear. На iOS7 это работает всеми вышеперечисленными способами. Поэтому попробуйте использовать его в любых других методах при работе с viewController. Я подтверждаю, что он работает для меня на iOS8, когда я нажимаю на какую-то кнопку внутри представления.
Сихад Бегович
8
Можно подтвердить, что это не сработает в iOS8 в viewDidLoad и viewWillAppear, и поместив его в viewwilllayoutgubviews, добился
цели
47

Я обнаружил, что установка жеста на отключение не всегда работает. Это работает, но для меня это только после того, как я однажды использовал backgesture. Во второй раз это не вызовет спину.

Исправление для меня состояло в том, чтобы делегировать жест и реализовать метод shouldbegin, возвращающий 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
источник
1
Спасибо! Это необходимо для полного отключения обратного пролистывания. Это все еще существует в iOS 8, и пахнет как ошибка Apple.
Эрик Чен,
Спасибо, похоже, единственное, что сработало.
Бен
Я не знаю, почему, но контроллер представления в моем приложении по какой-то неизвестной причине зависал на этом жесте спины ... это спасло меня от нахождения его, так как мне не требовался этот жест спины, и поэтому я отключил использование этого кода .. +1
Ахсан Эбрагим
1
@AhsanEbrahim, когда начинается обратный жест, viewWillAppearвызывается в представлении позади текущего представления. Это может вызвать хаос в логике кода, так как текущее представление все еще активно. Может быть причиной вашего крушения.
phatmann
Нужны ли линии enabledда / нет? Вы возвращаетесь NOиз gestureRecognizerShouldBegin, разве этого не достаточно?
ToolmakerSteve
30

Просто удалите распознаватель жестов из NavigationController. Работа в iOS 8.

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)])
    [self.navigationController.view removeGestureRecognizer:self.navigationController.interactivePopGestureRecognizer];
Владимир Самойлов
источник
единственное решение, которое действительно работает в ios 8 и 9
Kappe
7
Также работает в iOS 10, это должен быть принятый ответ. Кстати, если вы хотите его снова включить, сделайте [self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]где-нибудь.
ooops
22

Начиная с iOS 8 принятый ответ больше не работает. Мне нужно было остановить движение, чтобы отклонить жест на экране основной игры, поэтому реализовал это:

- (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
Хотя это работает с iOS8, я получаю предупреждение в строке * .delegate = self; Заявление: присвоение идентификатору <UIGestureRecognizerDelegate> 'из несовместимого типа' ViewController * const __strong '
Дэвид Дуглас
2
Начиная с iOS8, принятый ответ по-прежнему работает, как и ожидалось. Вы, вероятно, делаете что-то еще не так ..
Александр G
Удалось наполовину работать, вызвав принятый ответ в viewWillLayoutSubviews. Тем не менее, из-за перелистывания страница снова вызывала viewDidLoad, поэтому вернулась к моему ответу выше
Чарли Селигман,
@DavidDouglas: возможно, вы могли бы устранить предупреждение с помощью этого кода: __weak __typeof (self) theSafeSelf = self? Затем установите делегата на SafeSelf.
lifjoy
1
@DavidDouglas: вам нужно добавить <UIGestureRecognizerDelegate> к интерфейсу, чтобы избавиться от этого предупреждения
примхало
20

Я немного уточнил ответ Твана, потому что:

  1. ваш контроллер представления может быть установлен в качестве делегата для других распознавателей жестов
  2. установка делегата nilприводит к зависанию проблем, когда вы возвращаетесь к корневому контроллеру представления и делаете жест смахивания перед переходом в другое место.

В следующем примере предполагается 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 «установка делегата на ноль приводит к зависанию проблем, когда вы возвращаетесь к корневому контроллеру представления и делаете жест смахивания перед переходом в другое место».
Albertamg
10

Пожалуйста, установите это в 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
источник
9

Для Свифта:

navigationController!.interactivePopGestureRecognizer!.enabled = false
iPhone 7
источник
12
Это работает, хотя я бы предложил использовать необязательную цепочку вместо принудительного развертывания. ? например self.navigationController .interactivePopGestureRecognizer .isEnabled = ложь
Уомбл
5

у меня работает в ios 10 и позже:

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

}

он не работает с методом viewDidLoad ().

логика
источник
5

РЕДАКТИРОВАТЬ

Если вы хотите управлять функцией пролистывания назад для определенных контроллеров навигации, рассмотрите возможность использования SwipeBack .

С этим вы можете установить navigationController.swipeBackEnabled = NO .

Например:

#import <SwipeBack/SwipeBack.h>

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

Его можно установить через CocoaPods .

pod 'SwipeBack', '~> 1.0'

Я прошу прощения за отсутствие объяснения.

devxoul
источник
6
При продвижении проекта, с которым вы связаны, вы должны раскрыть свою принадлежность к нему.
2
Более того, единственная цель вашего проекта - вручную включить жест смахивания, когда системный по умолчанию не работает, тогда как вопрос состоит в том, как отключить этот общесистемный жест, поэтому даже если вы установите, self.navigationController.swipeBackEnabled = NOя уверен, что это отключит только ваш жест библиотеки назад, но системный будет включен.
1
Извините за мой короткий ответ, я только что отредактировал свой ответ с дополнительной информацией: «полезно для определенных контроллеров навигации». Спасибо!
devxoul
Похоже, что использовать Swizzle, который больше не допускается. iOS8?
Мэтт
1
@devxoul Я извиняюсь! Я думал, что прочитал что-то некоторое время назад, говоря, что пьянство больше не разрешено. Однако я не могу найти ничего, что говорит это. Думаю, я не прав.
Мэтт
4

Мой метод. Один распознаватель жестов, чтобы управлять ими всеми:

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

Важно: не сбрасывайте делегат нигде в стеке навигации: navigationController!.interactivePopGestureRecognizer!.delegate = nil

SoftDesigner
источник
3

Это путь на Swift 3

работает для меня

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
Tayo119
источник
3

Все эти решения манипулируют распознавателем жестов Apple так, как они не рекомендуют. Мне только что сказал друг, что есть лучшее решение:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

где myPanGestureRecognizer - это распознаватель жестов, который вы используете, например, чтобы показать свое меню. Таким образом, распознаватель жестов Apple не включается ими, когда вы нажимаете на новый контроллер навигации, и вам не нужно полагаться на случайные задержки, которые могут сработать слишком рано, если ваш телефон находится в спящем режиме или под большой нагрузкой.

Оставив это здесь, потому что я знаю, я не запомню это в следующий раз, когда мне это понадобится, и тогда у меня будет решение проблемы здесь.

uliwitness
источник
3

Swift 5, Swift 4.2 можно использовать код ниже.

// disable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// enable
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
Zgpeace
источник
2

Ни один из приведенных ответов не помог мне решить проблему. Размещение моего ответа здесь; может быть полезным для кого-то

Объявите private var popGesture: UIGestureRecognizer?как глобальную переменную в вашем viewcontroller. Затем реализовать код в viewDidAppear и 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!)
    }
}

Это отключит пролистывание назад в iOS v8.x и далее.

Августин П.А.
источник
Я пытаюсь представить, при каких обстоятельствах это будет работать, но Джек не будет. Вы говорите, что пробовали все остальные ответы: что пошло не так, когда вы попробовали Джек?
ToolmakerSteve
С другой стороны, это кажется проще, чем у Джека, так что, возможно, это не важно. Решил, что мне это нравится, потому что не нужно объявлять мой класс делегатом или манипулировать им interactivePopGestureRecognizer.delegate.
ToolmakerSteve
Кстати, код может быть упрощен. Удалить if( .. respondsToSelector ... Следующая строка устанавливает popGesture в распознаватель или в ноль. Затем используйте его значение: if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture ).
ToolmakerSteve
2

Это работает 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;
  });

Многие проблемы могут быть решены с помощью доброго dispatch_after .

Хотя обратите внимание, что это решение потенциально небезопасно, пожалуйста, используйте ваши собственные аргументы.

Обновить

Для iOS 8.1 время задержки должно составлять 0,5 секунды

В iOS 9.3 больше не требуется никаких задержек, это работает, просто поместив это в ваш viewDidLoad:
(TBD, если работает на iOS 9.0-9.3)

navigationController?.interactivePopGestureRecognizer?.enabled = false
Дэнни П
источник
Если вы не знаете, когда в представлении установлен распознаватель жестов, ожидание произвольного количества времени для его отключения может работать, а может и не работать.
Кальперин
@kalperin это не гарантированно работает, хотя иногда это очень удобное решение. Используйте свои собственные рассуждения.
Дэнни П
У меня работает версия выше, чем iOS 8.1 :)
iChirag
viewDidLoadплюс задержка - рискованная практика программирования. Плохая привычка начинать. Что если пользователь запустит свайп до того, как начнется ваш отложенный звонок? Там нет безопасного времени, которое гарантированно будет достаточно долго, но не слишком долго. Вот почему другие ответы, опубликованные задолго до вашего, предлагают разместить код в viewDidAppear. Это гарантирует, что все установлено. Не изобретайте произвольные задержки; используйте последовательность вызовов Apple, как задумано.
ToolmakerSteve
1
@iChirag верно. Я отметил, что для 8,1 вам нужно 0,5 секунды задержки
Дэнни П
1

Для Swift 4 это работает:

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
источник
Вы не должны переопределять делегат интерактивного жеста, так как это приведет к недокументированному поведению
Джош Бернфельд,
Я думаю, что это не переопределение делегата, а просто изменение логической переменной, которую они предоставили для этой цели, так что это не будет проблемой
Lok SN
0

У меня это работало для большинства контроллеров представления.

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

Это не работало для некоторых контроллеров представления, таких как UIPageViewController. На странице управления контентом UIPageViewController ниже код работал для меня.

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
}

На UIGestureRecognizerDelegate,

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
   if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
      return false
}
      return true
}
Фарис Мухаммед
источник