Як відключити рух пальцем назад у UINavigationController на iOS 7


326

У iOS 7 Apple додала нову навігаційну поведінку за замовчуванням. Ви можете провести пальцем з лівого краю екрана, щоб повернутися до навігаційного стеку. Але в моєму додатку така поведінка суперечить моєму користувальницькому лівому меню. Отже, чи можна відключити цей новий жест у UINavigationController?



2
Я також з’ясував, що якщо встановити navigationItem.hidesBackButton = true, цей жест також буде відключений. У моєму випадку я реалізував користувальницьку кнопку назад і додав її якleftBarButtonItem
Umair

Відповіді:


586

Я знайшов рішення:

Завдання-C:

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

Швидкий 3+:
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false


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

Я з’ясував, що встановити жест на інвалід не завжди працює. Це спрацює, але для мене це зробило лише після того, як я колись скористався backsteure. Вдруге це не призведе до зворотної дії.

Виправленням для мене було делегування жесту та реалізація методу bebegin для повернення 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
Дякую! Це потрібно для повного відключення пальця назад. Він все ще існує в 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; заявляючи: Присвоєння id <UIGestureRecognizerDelegate> 'від несумісного типу' ViewController * const __strong '
Девід Дуглас

2
Що стосується iOS8, прийнята відповідь все ще працює як очікувалося. Ви, мабуть, робите щось інше неправильно ..
Олександр G

Нам вдалося зробити його напівпрацюючим, викликавши прийняту відповідь у viewWillLayoutSubviews. Однак перехід пальцем все-таки викликав "viewDidLoad", щоб повернутися до моєї відповіді вище
Charlie Seligman,

@DavidDouglas: можливо, ви могли б усунути попередження за допомогою цього коду: __weak __typeof (self) theSafeSelf = self? Потім встановіть делегата на theSafeSelf.
lifjoy

1
@DavidDouglas: Вам потрібно додати <UIGestureRecognizerDelegate> до інтерфейсу, щоб позбутися цього попередження
primehalo

20

Я трохи уточнив відповідь Twan, тому що:

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

9

Для Swift:

navigationController!.interactivePopGestureRecognizer!.enabled = false

12
Це працює, хоча я б запропонував використовувати додаткові ланцюжки замість сили розкручування. наприклад, self.navigationController? .interactivePopGestureRecognizer? .isEnabled = false
Womble

5

він працює для мене в ios 10 і пізніших версіях:

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

}

він не працює на метод viewDidLoad ().


5

EDIT

Якщо ви хочете керувати функцією зворотного переведення для конкретних навігаційних контролерів, розгляньте можливість використання SwipeBack .

За допомогою цього ви можете встановити navigationController.swipeBackEnabled = NO.

Наприклад:

#import <SwipeBack/SwipeBack.h>

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

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

pod 'SwipeBack', '~> 1.0'

Я прошу вибачення за відсутність пояснень.


6
Під час просування проекту, з яким ви співпрацюєте, ви повинні розкрити свою приналежність до нього.

2
Крім того, мета вашого проекту полягає в тому, щоб вручну ввімкнути жест пальцем, коли система за замовчуванням не працює, тоді як у питанні задається питання, як відключити цей системний жест, тому навіть якщо ви встановите, self.navigationController.swipeBackEnabled = NOя впевнений, що це лише відключить ваш Відведення пальцем назад у бібліотеку, але система все одно буде включена.

1
Вибачте за свою коротку відповідь, я щойно відредагував свою відповідь додатковою інформацією: "корисно для конкретних контролерів навігації". Дякую!
devxoul

Здається, використовувати свинець, який більше не дозволений. 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


3

Це шлях на Swift 3

працює для мене

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

3

Усі ці рішення маніпулюють розпізнавачем жестів Apple таким чином, який вони не рекомендують. Мені щойно сказав друг, що є краще рішення:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

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

Залишаючи це тут, бо знаю, що не згадаю про це наступного разу, коли мені це потрібно, і тоді у мене буде вирішення питання тут.


3

swift 5, swift 4.2 може використовувати код нижче.

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

2

Жодна із наведених відповідей не допомогла мені вирішити питання. Опублікувавши тут свою відповідь; може бути корисним для когось

private var popGesture: UIGestureRecognizer?Заявіть про глобальну змінну у своєму контролері перегляду. Потім реалізувати код в 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 це не гарантовано, хоча це дуже зручне рішення. Використовуйте власні міркування.
Dannie P

Для мене працює версія, більша за iOS 8.1 :)
iChirag

viewDidLoadплюс затримка - це ризикована практика програмування. Погана звичка починати. Що робити, якщо користувач запускає пальцем до початку затримки виклику? Немає безпечного часу, який гарантовано буде досить довгим, але не надто довгим. Ось чому інші відповіді, розміщені задовго до вашого, пропонують розмістити код viewDidAppear. Це забезпечує все, що встановлено. Не вигадуйте довільних затримок; використовувати послідовність дзвінків Apple за призначенням.
ToolmakerSteve

1
@iChirag правда. Я зазначив, що для 8,1 вам потрібно затримка на 0,5 секунди
Dannie P

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
    }

}

Не слід перекривати інтерактивного делегата жеста попсу, оскільки це спричинить незадокументовану поведінку
Джош Бернфельд

Я думаю, що це не дуже перевершує делегата, а просто модифікує булеву змінну, яку вони надали саме для цієї мети, тому це не буде проблемою
Lok SN

0

Це працювало на мене для більшості контролерів перегляду.

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

Це не працює для деяких контролерів перегляду, таких як UIPageViewController. На сторінці UIPageViewController pagecontentviewcontrolller нижче код працював для мене.

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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.