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

时间:2016-01-22 08:56:00

标签: ios objective-c uinavigationcontroller uinavigationbar uinavigationitem

我从here得到了相反的问题。 默认情况下,在iOS7中,UINavigationController堆栈的后滑动手势可能会弹出显示的ViewController。现在我只对所有self.navigationItem.leftBarButtonItem的所有ViewControllers样式进行了统一。

以下是代码:

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

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

谢谢!

16 个答案:

答案 0 :(得分:63)

首先在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添加到视图控制器。

答案 1 :(得分:46)

当我设置代理

时,它适用于我
self.navigationController.interactivePopGestureRecognizer.delegate = self;

然后实施

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

答案 2 :(得分:46)

您需要处理两种情况:

  1. 当您将新视图推入堆栈时
  2. 当您显示根视图控制器
  3. 如果您只需要一个可以使用的基类,这里是一个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,则可以编写代理转发器similar to this answer

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

答案 3 :(得分:43)

它适用于我 Swift 3

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

并在ViewDidLoad中:

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

答案 4 :(得分:11)

这是在 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;
} }

答案 5 :(得分: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等以前的控制器。

答案 6 :(得分:3)

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

保持它的最好方法是继承UINavigationViewController并将自己设置为interactivePopGestureRecognizer委托;然后你可以从gestureRecognizerShouldBegin返回YES以允许滑动。

例如,这是在AHKNavigationController

中完成的

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

答案 7 :(得分:3)

斯威夫特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)
}

答案 8 :(得分:3)

如果您希望在应用程序中的任何位置都具有这种行为,并且不想向单个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时,您都会获得理想的体验。

答案 9 :(得分:1)

This回答,但有故事板支持。

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
}

答案 10 :(得分:0)

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

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

答案 11 :(得分:0)

我们正在努力解决some old bugs,因为它是“有意设计的”,因此尚未得到解决。在尝试取消interactivePopGestureRecognizer的委托时,我遇到了其他地方提到的冻结问题@iwasrobbed,这似乎应该可行。如果您想重新使用backBarButtonItem,您可以可以自定义滑动行为。

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

答案 12 :(得分:0)

大多数答案都与在代码上执行有关。但我给你一个可以在Storyboard上运行的软件。是!您没看错。

  • 单击主UINavigationController并导航至其Identity Inspector标签。

  • User Defined Runtime Attributes下,将名为interactivePopGestureRecognizer.enabled的单个运行时属性设置为true。或者以图形方式,您必须启用下图所示的复选框。

就是这样。你很好您的后向手势将一直有效。

Image displaying the property that out to be set

答案 13 :(得分:0)

Swift 5,在 viewDidLoad 方法中只添加这两个:

override func viewDidLoad() {
    super.viewDidLoad()

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

答案 14 :(得分:0)

我在启用和禁用与弹出视图控制器的滑动交互时遇到问题。

我有一个基本的导航控制器,我的应用流程就像推送 Splash VC、推送 Main VC,然后推送 Some VC 一样。

我想滑动以从 Some VC 返回到 Main VC。还要禁用滑动以防止从主 VC 返回飞溅。

经过下面的一些尝试对我有用。

  1. 在主 VC 中编写扩展以禁用滑动
extension MainViewController : UIGestureRecognizerDelegate{
    
    func disableSwipeToPop() {
        self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
        self.navigationController?.interactivePopGestureRecognizer?.delegate = self
    }
    
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        
        if gestureRecognizer == self.navigationController?.interactivePopGestureRecognizer {
            return false
        }
        return true
    }
}
  1. 在主 VC 的 viewDidAppear 上调用 disableSwipeToPop() 方法
override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.disableSwipeToPop()
}
  1. 在 Some VC 中编写一个扩展以启用滑动以弹出 Some VC
extension SomeViewController{
    
    func enableSwipeToPop() {
        self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
        self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
    }
    
}
  1. 在某些 VC 的 viewDidLoad 上调用 enableSwipeToPop() 方法
override func viewDidLoad() {
        super.viewDidLoad()
        self.enableSwipeToPop()
}

就是这样。此外,如果您尝试在 viewWillAppear 上禁用滑动,当用户停止滑动以取消操作时,您可能会失去再次滑动的能力。

答案 15 :(得分:-1)

对于仍然遇到此问题的人,请尝试将两行分开,如下所示。

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

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

显然,在我的应用中,

  

interactivePopGestureRecognizer!.isEnabled

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