设置leftBarButtonItem后如何在UINavigationController中启用向后/向左滑动手势?


75

我从这里得到了相反的问题。默认情况下iOS7UINavigationController堆栈的向后轻扫手势可能会弹出呈现的内容ViewController。现在我只是统一了所有self.navigationItem.leftBarButtonItem样式ViewControllers

这是代码:

self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:LOADIMAGE(@"back_button") style:UIBarButtonItemStylePlain target:self action:@selector(popCurrentViewController)];

之后,将navigationController.interactivePopGestureRecognizer被禁用。如何在不删除自定义的情况下启用弹出手势leftBarButtonItem

谢谢!


相同的问题已在此处
-IAN

@ian谢谢!这意味着所有屏幕滑动手势都是向后滑动,我认为这不是个好主意。
Itachi 2016年

Answers:


81

首先在viewDidLoad中设置委托:

self.navigationController.interactivePopGestureRecognizer.delegate = self;

然后在按下时禁用手势:

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [super pushViewController:viewController animated:animated];
    self.interactivePopGestureRecognizer.enabled = NO;
}

并在viewDidDisappear中启用:

self.navigationController.interactivePopGestureRecognizer.enabled = YES;

另外,添加UINavigationControllerDelegate到您的视图控制器。


4
我忘了说您应该将UINavigationControllerDelegate添加到您的视图控制器。
Lumialxk '16

谢谢!我也是刚刚在SO中找到了解决方案!(我在问之前没有搜索到足够的结果,这

2
为什么要设置识别器的委托人?您没有提到协议方法的任何实现吗?
hfossli

1
这是行不通的。@hfossli的解决方案效果很好。虽然我在Swift3中尝试过。
Shubham Naik

我们如何才能使它通用并添加现有项目?我们肯定不想在每个ViewController上都这样做……
Amber K

75

您需要处理两种情况:

  1. 当您将新视图推入堆栈时
  2. 当显示根视图控制器时

如果您只需要可以使用的基类,则可以使用Swift 3版本:

import UIKit

final class SwipeNavigationController: UINavigationController {

    // MARK: - Lifecycle

    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        delegate = self
    }

    required init?(coder aDecoder: NSCoder) { 
        super.init(coder: aDecoder) 

        delegate = self 
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // This needs to be in here, not in init
        interactivePopGestureRecognizer?.delegate = self
    }

    deinit {
        delegate = nil
        interactivePopGestureRecognizer?.delegate = nil
    }

    // MARK: - Overrides

    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        duringPushAnimation = true

        super.pushViewController(viewController, animated: animated)
    }

    // MARK: - Private Properties

    fileprivate var duringPushAnimation = false

}

// MARK: - UINavigationControllerDelegate

extension SwipeNavigationController: UINavigationControllerDelegate {

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        guard let swipeNavigationController = navigationController as? SwipeNavigationController else { return }

        swipeNavigationController.duringPushAnimation = false
    }

}

// MARK: - UIGestureRecognizerDelegate

extension SwipeNavigationController: UIGestureRecognizerDelegate {

    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard gestureRecognizer == interactivePopGestureRecognizer else {
            return true // default value
        }

        // Disable pop gesture in two situations:
        // 1) when the pop animation is in progress
        // 2) when user swipes quickly a couple of times and animations don't have time to be performed
        return viewControllers.count > 1 && duringPushAnimation == false
    }
}

如果最终需要UINavigationControllerDelegate在另一个类中充当,则可以编写类似于此答案的委托转发器。

改编自Objective-C中的源代码:https : //github.com/fastred/AHKNavigationController


这是一个很好的解决方案
Andrea.Ferrando

7
确实很好用,谢谢-但是您也可以(也应该实现)required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) delegate = self }与情节
提要

我添加了情节提要板支持:stackoverflow.com/a/49750785/129202似乎可以工作,但是可以对其进行编辑以解决任何问题。
强尼

1
如果我不需要在某些屏幕上向后滑动该怎么办。在该屏幕上我需要做什么?我为此委托方法返回了false。 func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { 并在viewwillAppear和消失方法中将true设置为false均无效self.navigationController?.interactivePopGestureRecognizer?.isEnabled。有什么办法吗?
萨拉斯

2
我怎么称呼这堂课?
Marlhex

71

当我设置代表时对我有用

self.navigationController.interactivePopGestureRecognizer.delegate = self;

然后实施

迅速

extension MyViewController:UIGestureRecognizerDelegate {
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

目标C

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

1
您能否指出任何与“回头滑动”功能相关的Apple文档?
2016年


2
不幸的是,如果在推送动画期间或在根视图控制器上滑动时,这将冻结。我在此处发布了一个修复版本:stackoverflow.com/a/43433530/308315
iwasrobbed

2
这可行!但为什么?谁能提供更多解释?为什么要求这个手势识别器因另一个手势识别器而失败,以某种方式使它真正地识别了该手势?
igrek

1
仍在拯救生命!😍
Codetard

53

它对我有用Swift 3

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

并在ViewDidLoad中:

    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true

9
另外,如果您有UIViewController,则也应该继承UIGestureRecognizerDelegate。
haris

8
这对我有用,但是我不需要使用.isEnabled线路。
克利夫顿·拉布鲁姆

4
不幸的是,如果在推送动画期间或在根视图控制器上滑动时,这将冻结。我在这里发布了一个修复版本:stackoverflow.com/a/43433530/308315
iwasrobbed'Apr 16'17

14

这是在iOS 10,Swift 3中启用/禁用滑动弹出视图控制器的最佳方法:

对于第一个屏幕[您要禁用滑动手势的位置]:

class SignUpViewController : UIViewController,UIGestureRecognizerDelegate {

//MARK: - View initializers
override func viewDidLoad() {
    super.viewDidLoad()
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    swipeToPop()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

func swipeToPop() {

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true;
    self.navigationController?.interactivePopGestureRecognizer?.delegate = self;
}

func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {

    if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
        return false
    }
    return true
} }

对于中间屏幕[您要启用滑动手势的位置]:

class FriendListViewController : UIViewController {

//MARK: - View initializers
override func viewDidLoad() {

    super.viewDidLoad()
    swipeToPop()
}

func swipeToPop() {

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true;
    self.navigationController?.interactivePopGestureRecognizer?.delegate = nil;
} }

3
不幸的是,如果在推送动画期间或在根视图控制器上滑动时,这将冻结。我在这里发布了一个修复版本:stackoverflow.com/a/43433530/308315
iwasrobbed

@iwasrobbed我在根视图控制器上检查了上面的代码,它对我来说很好用,我不知道为什么它在您端失败。感谢您的更新版本
Bean先生

1
这是一个完美的答案,@iwasrobbed您的代码不起作用
Nitin Bhatt

@ NitinBhatt,cn如果答案对您有用,请给我+1。谢谢
Bean先生

10

我不需要为其添加gestureRecognizer函数。对于我来说,在viewDidLoad中添加以下代码块就足够了:

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
}

这对我适用于iOS 13.3!该delegate = nil部分是关键。
纳撒尼尔·布鲁默

1
检查以下答案以避免错误:stackoverflow.com/a/61668408/1887537
Vladimir Shutyuk '20

6

斯威夫特3:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return (otherGestureRecognizer is UIScreenEdgePanGestureRecognizer)
}

3
不幸的是,如果在推送动画期间或在根视图控制器上滑动时,这将冻结。我在这里发布了一个修复版本:stackoverflow.com/a/43433530/308315
iwasrobbed

6

在Swift中,您可以执行以下代码

import UIKit
extension UINavigationController: UIGestureRecognizerDelegate {

    open override func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}

上面的代码有助于快速返回到Facebook,Twitter等以前的控制器。


4

设置自定义后退按钮可禁用后退滑动功能。

保持它最好的方法是继承UINavigationViewController并设置自己为interactivePopGestureRecognizer委托。那么您可以从中返回YES,gestureRecognizerShouldBegin以允许向后滑动。

例如,这是在AHKNavigationController中完成的

还有一个Swift版本:https : //stackoverflow.com/a/43433530/308315


也禁止弹出时的导航栏被隐藏
Zaporozhchenko阿列克

4

如果您希望在应用程序中的任何地方都具有这种行为,并且不想向个人viewDidAppear等添加任何内容,则应该创建一个子类

class QFNavigationController:UINavigationController, UIGestureRecognizerDelegate, UINavigationControllerDelegate{
    override func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
        delegate = self
    }

    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        super.pushViewController(viewController, animated: animated)
        interactivePopGestureRecognizer?.isEnabled = false
    }

    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        interactivePopGestureRecognizer?.isEnabled = true
    }

    // IMPORTANT: without this if you attempt swipe on
    // first view controller you may be unable to push the next one
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }

}

现在,无论何时使用,QFNavigationController您都可以获得理想的体验。


3

这个答案,但有故事板支持。

class SwipeNavigationController: UINavigationController {

    // MARK: - Lifecycle

    override init(rootViewController: UIViewController) {
        super.init(rootViewController: rootViewController)
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)

        self.setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.setup()
    }

    private func setup() {
        delegate = self
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // This needs to be in here, not in init
        interactivePopGestureRecognizer?.delegate = self
    }

    deinit {
        delegate = nil
        interactivePopGestureRecognizer?.delegate = nil
    }

    // MARK: - Overrides

    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        duringPushAnimation = true

        super.pushViewController(viewController, animated: animated)
    }

    // MARK: - Private Properties

    fileprivate var duringPushAnimation = false
}

0

我们都在解决一些尚未解决的旧错误,可能是因为它是“有意设计的”。当试图取消interactivePopGestureRecognizer似乎应该起作用的委托时,我遇到了在其他地方描述的@iwasrobbed冻结问题。如果您想重新考虑滑动行为backBarButtonItem可以使用自定义。

隐藏interactivePopGestureRecognizer时,我还遇到了无法工作的UINavigationBar情况。如果您担心隐藏导航栏,请在实现错误的解决方法之前重新考虑您的设计。


0

大多数答案都与在代码上执行有关。但是,我会给您一个适用于Storyboard的工具。是! 您没看错。

  • 单击mainUINavigationController并导航到它的Identity Inspector选项卡。

  • User Defined Runtime Attributes设置称为单一的运行特性interactivePopGestureRecognizer.enabledtrue。或者以图形方式,您必须启用该复选框,如下图所示。

而已。你很好 您的后向手势将一直有效。

显示要设置的属性的图像


-1

对于仍然对此有疑问的人,请尝试如下将两行分开。

override func viewDidLoad() {
    self.navigationController!.interactivePopGestureRecognizer!.delegate = self
    ...

override func viewWillAppear(_ animated: Bool) {
    self.navigationController!.interactivePopGestureRecognizer!.isEnabled = true
    ...

显然,在我的应用中

InteractivePopGestureRecognizer!.isEnabled

false在由于某种原因显示视图之前重置为。

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.