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

Posted

技术标签:

【中文标题】如何在 iOS 7 上的 UINavigationController 中禁用向后滑动手势【英文标题】:How to disable back swipe gesture in UINavigationController on iOS 7 【发布时间】:2013-06-17 01:31:35 【问题描述】:

ios 7 中,Apple 添加了新的默认导航行为。您可以从屏幕的左边缘滑动以返回导航堆栈。但在我的应用程序中,这种行为与我的自定义左侧菜单冲突。那么,是否可以在 UINavigationController 中禁用这个新手势?

【问题讨论】:

How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem? 我还发现如果你设置navigationItem.hidesBackButton = true,这个手势也会被禁用。就我而言,我实现了一个自定义后退按钮并添加为leftBarButtonItem 【参考方案1】:
self.navigationController.pushViewController(VC, animated: Bool)

打电话

self.navigationController.setViewContollers([VC], animated: Bool)

setViewControllers 替换堆栈上的所有 VC,而不是在顶部添加新控制器。这意味着新设置的VC是根VC,用户无法返回。

当您只想禁用单个 VC 上的滑动并保留其他 VC 的滑动到背面时,这是最有效的。

如果您希望用户能够返回,而不是通过滑动,请不要使用此方法,因为它会禁用所有返回(因为没有 VC 可以返回)

【讨论】:

【参考方案2】:

我找到了解决办法:

目标-C:

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

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

【讨论】:

当然,如果您支持旧版本的iOS,则需要检查新方法的可用性。 有没有办法禁用它以获得视图的药水? 您可以在viewDidAppear: / viewDidDisappear 上使用enable / disable 识别器。或者,您可以使用更复杂的逻辑实现UIGestureRecognizerDelegate 协议并将其设置为recognizer.delegate 属性。 在 iOS8 上,设置 self.navigationController.interactivePopGestureRecognizer.enabled 属性在以下视图的方法中不起作用:viewDidLoadviewWillAppearviewDidAppearviewDidDisappear,但在方法 viewWillDisappear 中起作用。在 iOS7 上,它适用于上述所有方法。因此,在视图控制器上工作时尝试在任何其他方法中使用它,当我单击视图内的某个按钮时,我确认它在 iOS8 上适用于我。 可以确认这在 iOS8 中的 viewDidLoad 和 viewWillAppear 中不起作用,将其放入 viewwilllayoutgubviews 就可以了【参考方案3】:

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

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

【讨论】:

【参考方案4】:

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

[navigationController.interactivePopGestureRecognizer requireGestureRecognizerToFail: myPanGestureRecognizer];

其中 myPanGestureRecognizer 是您正在使用的手势识别器,例如显示您的菜单。这样,当您按下新的导航控制器时,Apple 的手势识别器不会被他们重新打开,并且您无需依赖在手机进入睡眠状态或负载过重时可能会过早触发的 hacky 延迟。

把这个留在这里是因为我知道下次我需要它时我不会记住它,然后我会在这里找到问题的解决方案。

【讨论】:

【参考方案5】:

它适用于大多数视图控制器。

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

【讨论】:

【参考方案6】:

对于 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
    


【讨论】:

您不应覆盖交互式弹出手势委托,因为它会导致未记录的行为 我认为这并没有真正覆盖委托,而只是修改了他们为此目的提供的布尔变量,所以这不是问题【参考方案7】:

编辑

如果您想管理特定导航控制器的向后滑动功能,请考虑使用SwipeBack。

有了这个,你可以设置navigationController.swipeBackEnabled = NO

例如:

#import <SwipeBack/SwipeBack.h>

- (void)viewWillAppear:(BOOL)animated

    navigationController.swipeBackEnabled = NO;

可以通过CocoaPods安装。

pod 'SwipeBack', '~> 1.0'

我很抱歉没有解释。

【讨论】:

在宣传您参与的项目时,您必须披露您与该项目的隶属关系。 此外,您的项目的唯一目的是在默认系统不起作用时手动启用滑动手势,而问题询问如何禁用该系统范围的手势,所以即使您设置 @987654326 @我很确定这只会禁用您图书馆的向后滑动手势,但系统的手势仍将启用。 对不起,我的简短回答,我刚刚用附加信息编辑了我的答案:“对特定导航控制器有用”。谢谢! 它似乎使用了不再被允许的 swizzle。 iOS8? @devxoul 对不起!我想我前段时间读过一些东西,说不再允许调酒。但是,我找不到任何说明这一点的东西。我猜我错了。【参考方案8】:

对于斯威夫特:

navigationController!.interactivePopGestureRecognizer!.enabled = false

【讨论】:

这可行,但我建议使用可选链接而不是强制展开。例如self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false【参考方案9】:

请在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;

【讨论】:

【参考方案10】:

这是 Swift 3 的方式

为我工作

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false

【讨论】:

【参考方案11】:

它适用于 ios 10 及更高版本:

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


它不适用于 viewDidLoad() 方法。

【讨论】:

【参考方案12】:

我的方法。一个手势识别器来统治他们:

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

【讨论】:

【参考方案13】:

这适用于 iOS 8 的 viewDidLoad:

  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 虽然有时它是一个非常方便的解决方案,但不能保证它可以工作。用你自己的推理。 它适用于我的版本高于 iOS 8.1 :) viewDidLoad 加上延迟是一种冒险的编程实践。一个坏习惯开始。如果用户在您的延迟通话开始之前开始滑动怎么办?没有安全时间可以保证足够长但不会太长。这就是为什么早在您之前发布的其他答案建议将代码放在viewDidAppear 中。这确保了一切都已安装。不要发明任意延迟;按预期使用 Apple 的调用序列。 @iChirag 是的。我注意到,对于 8.1,您需要 0.5 秒延迟【参考方案14】:

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

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 及以后的滑动功能

【讨论】:

我试图想象在什么情况下这会起作用,但 Jack's 不会。您说您尝试了所有其他答案:当您尝试 Jack's 时出了什么问题? 另一方面,这似乎比杰克的简单,所以也许它并不重要。决定我喜欢这个,因为不必将我的班级声明为委托,也不必操纵interactivePopGestureRecognizer.delegate 顺便说一句,代码可以简化。删除if( .. respondsToSelector ..。下一行将 popGesture 设置为识别器或 nil。然后使用它的值:if (self.popGesture != nil) self.navigationController .. removeGestureRecognizer( self.popGesture )【参考方案15】:

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

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

【讨论】:

也适用于 iOS 10,这应该是公认的答案。顺便说一句,如果您想重新启用它,请在某处执行[self.navigationController.view addGestureRecognizer:self.navigationController.interactivePopGestureRecognizer]【参考方案16】:

我稍微完善了 Twan 的答案,因为:

    您的视图控制器可以设置为其他手势识别器的委托 将代理设置为 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 会导致挂起问题。”【参考方案17】:

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

【讨论】:

虽然这适用于 iOS8,但我在 *.delegate = self;声明:从不兼容的类型'ViewController *const __strong' 分配给 id' 从 iOS8 开始,接受的答案仍然按预期工作。你可能做错了什么.. 通过在 viewWillLayoutSubviews 中调用接受的答案,设法使其半工作。但是,刷卡确实导致页面再次调用“viewDidLoad”,因此恢复到我上面的答案 @DavidDouglas:也许您可以使用以下代码消除警告:__weak __typeof(self) theSafeSelf = self?然后将代理设置为 theSafeSelf。 @DavidDouglas:您需要将 添加到界面以消除该警告【参考方案18】:

我发现仅将手势设置为禁用并不总是有效。它确实有效,但对我来说,它只有在我使用过后手势之后才有效。第二次它不会触发背景手势。

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

【讨论】:

谢谢!这是完全禁用向后滑动所必需的。它仍然存在于 iOS 8 中,并且闻起来像 Apple 的 bug。 我不知道为什么,但我的应用程序中的一个视图控制器由于某种未知原因在这个后退手势上崩溃了。这让我没有找到它,因为我不需要这个后退手势,所以我禁用了这个代码.. +1 @AhsanEbrahim,当后退手势开始时,viewWillAppear 在当前视图后面的视图上被调用。由于当前视图仍处于活动状态,这可能会导致代码逻辑严重受损。可能是您崩溃的原因。 是否需要enabled 是/否行?你从gestureRecognizerShouldBegin返回NO,这还不够吗? 对于像我一样被困在这个问题上的任何人,如果您正在为拆分视图控制器中的主从视图执行此操作,您必须对self.navigationController.navigationController 执行相同操作。见***.com/a/50012503/5605365

以上是关于如何在 iOS 7 上的 UINavigationController 中禁用向后滑动手势的主要内容,如果未能解决你的问题,请参考以下文章

如何将按钮添加到 UINavigation 项?

应用程序的 ios 11 颜色(uinavigation)

关闭 UIAlertController 时调用 UINavigation 的 dismissViewControllerAnimated 方法

ios UINavigationController 导航栏

如何在Objective C中的UIViewController上添加UITabbar控制器和UINavigation bar

UINavigation 控制器上的完成按钮没有导航回来