如何在iOS 7的UINavigationController中禁用向后滑动手势


326

苹果在iOS 7中添加了新的默认导航行为。您可以从屏幕的左边缘轻扫以返回导航堆栈。但是在我的应用程序中,此行为与我的自定义左侧菜单冲突。因此,是否可以在UINavigationController中禁用此新手势?



2
我还发现,如果您设置navigationItem.hidesBackButton = true,此手势也会被禁用。就我而言,我实现了一个自定义的后退按钮,并添加为leftBarButtonItem
Umair

Answers:


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 / disableviewDidAppear:/上识别viewDidDisappear。或者,您可以UIGestureRecognizerDelegate使用更复杂的逻辑来实现协议并将其设置为recognizer.delegate属性。
ArtFeel 2013年

26
在iOS8上,设置self.navigationController.interactivePopGestureRecognizer.enabled属性不会在以下视图的方法的工作:viewDidLoadviewWillAppearviewDidAppearviewDidDisappear,但在方法作品viewWillDisappear。在iOS7上,它可用于上述所有方法。因此,当在viewController上工作时,尝试在任何其他方法中使用它,当我单击视图内的某些按钮时,我确认它在iOS8上适用。
Sihad Begovic

8
可以确认这在iOS8中的viewDidLoad和viewWillAppear中不起作用,将其放入viewwilllayoutgubviews可以成功解决问题
tonytastic

47

我发现将手势设置为“禁用”并不总是有效。它确实有效,但是对我来说,只有在我使用了手势之后才起作用。第二次它不会触发回退。

对我来说,解决的方法是委派该手势并实现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;
}

1
谢谢!这是完全禁用向后滑动所必需的。它仍然存在于iOS 8中,闻起来像苹果的bug。
Eric Chen

谢谢,看来这是唯一有效的方法。
2014年

我不知道为什么,但是由于某种未知原因,我的应用程序中的视图控制器在此后手势上崩溃了。这使我无法找到它,因为我不需要此后手势,所以我禁用了此代码。+1
Ahsan Ebrahim

1
当开始向后手势时,viewWillAppear在当前视图后面的视图上调用@AhsanEbrahim 。由于当前视图仍处于活动状态,因此这可能会导致代码逻辑混乱。可能是导致崩溃的原因。
phatmann

是否enabled需要是/否行?您NO从回来gestureRecognizerShouldBegin,还不够吗?
ToolmakerSteve

30

只需从NavigationController中删除手势识别器即可。在iOS 8中工作。

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

唯一在ios 8和ios 9中实际
可用的

7
在iOS 10中也可以使用,这应该是公认的答案。顺便说一句,如果您想重新启用它,请在[self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]某个地方进行。
哎呀,2016年

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上收到警告。说明:从不兼容类型'ViewController * const __strong'分配给id <UIGestureRecognizerDelegate>'
David Douglas

2
从iOS8开始,接受的答案仍然可以按预期工作。你可能会做别的事情错了..
亚历山大摹

通过在viewWillLayoutSubviews中调用接受的答案来设法使其半有效。但是,刷卡确实导致页面再次调用“ viewDidLoad”,因此恢复了上面的答案
Charlie Seligman

@DavidDouglas:也许您可以使用以下代码消除警告:__weak __typeof(self)theSafeSelf = self?然后将委托设置为SafeSelf。
lifjoy 2015年

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 “将委托设置为nil会导致挂起问题,当您返回到根视图控制器并在浏览其他位置之前进行轻扫手势时。”
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

编辑

如果要管理特定导航控制器的向后滑动功能,请考虑使用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?
马特(Matt)

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

所有这些解决方案都以他们不推荐的方式操纵苹果的手势识别器。一位朋友告诉我,有一个更好的解决方案:

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

其中myPanGestureRecognizer是用于例如显示菜单的手势识别器。这样一来,当您按下新的导航控制器时,Apple的手势识别器就不会再被打开,并且您不必依靠hacky的延迟,如果您的手机进入睡眠状态或负载过重,延迟可能会过早触发。

将此留在这里是因为我知道下次需要它时我将不记得了,然后在这里我将提供解决问题的方法。


3

swift 5,swift 4.2可以使用下面的代码。

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

2

给出的答案都没有帮助我解决问题。在这里发布我的答案;可能对某人有帮助

private var popGesture: UIGestureRecognizer?在视图控制器中声明为全局变量。然后在viewDidAppearviewWillDisappear方法中实现代码

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

另一方面,这似乎比Jack的方法简单,因此也许并不重要。决定我喜欢这个,因为不必将我的类声明为委托,也不必操纵interactivePopGestureRecognizer.delegate
ToolmakerSteve

顺便说一句,代码可以简化。删除if( .. respondsToSelector ..。下一行将popGesture设置为识别器,或设置为nil。然后使用其值: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:(
如果在iOS 9.0-9.3上可以使用,则为待定)

navigationController?.interactivePopGestureRecognizer?.enabled = false

除非您知道何时将手势识别器安装在视图上,否则等待任意时间禁用它可能会或可能不会起作用。
kalperin '16

@kalperin虽然有时是非常方便的解决方案,但不能保证能正常工作。使用您自己的推理。
丹妮·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
    }

}

您不应覆盖交互式弹出手势委托,因为它会导致未记录的行为
Josh Bernfeld '18

我认为这并不是真的要重写委托,而只是修改他们为此目的提供的布尔变量,所以这不会有问题
Lok SN

0

对于大多数视图控制器而言,它对我都有效。

self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

对于某些视图控制器(如UIPageViewController),它不起作用。在UIPageViewController的pagecontentviewcontroller上,以下代码对我有用。

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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.