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

Ita*_*chi 59 objective-c uinavigationbar uinavigationcontroller uinavigationitem ios

我从这里得到了相反的问题.默认情况下iOS7,UINavigationController堆栈的后滑动手势可以弹出呈现的内容ViewController.现在我只是穿着所有self.navigationItem.leftBarButtonItem风格的制服ViewControllers.

这是代码:

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

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

谢谢!

Lum*_*lxk 67

首先在viewDidLoad中设置委托:

self.navigationController.interactivePopGestureRecognizer.delegate = self;
Run Code Online (Sandbox Code Playgroud)

然后在推送时禁用手势:

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

并在viewDidDisappear中启用:

self.navigationController.interactivePopGestureRecognizer.enabled = YES;
Run Code Online (Sandbox Code Playgroud)

此外,添加UINavigationControllerDelegate到您的视图控制器.

  • 我忘了说你应该将UINavigationControllerDelegate添加到你的视图控制器. (3认同)
  • 为什么要设置识别器的委托人?您没有提及协议方法的任何实现吗? (2认同)

iwa*_*bed 59

您需要处理两种情况:

  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
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您最终需要UINavigationControllerDelegate在另一个类中执行操作,则可以编写类似于此答案的委托转发器.

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

  • 它确实很好用,谢谢 - 但你可以/也应该实现````init init?(编码器aDecoder:NSCoder){super.init(编码器:aDecoder)委托=自我}```用于故事板:) (7认同)
  • 我如何称呼这个班级? (2认同)

hfo*_*sli 51

当我设置委托时,它适用于我

self.navigationController.interactivePopGestureRecognizer.delegate = self;
Run Code Online (Sandbox Code Playgroud)

然后实施

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

  • 不幸的是,如果您在推送动画期间或在根视图控制器上滑动,这将冻结。我在这里发布了一个修复版本:http://stackoverflow.com/a/43433530/308315 (2认同)
  • 这可行!但为什么?谁能提供更多解释?为什么要求这个手势识别器因另一个手势识别器而失败,以某种方式使它真正地识别了该手势? (2认同)

小智 48

它适用于我Swift 3:

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
Run Code Online (Sandbox Code Playgroud)

并在ViewDidLoad中:

    self.navigationController?.interactivePopGestureRecognizer?.delegate = self
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
Run Code Online (Sandbox Code Playgroud)

  • 另外如果你有一个UIViewController,你也应该继承UIGestureRecognizerDelegate. (9认同)
  • 这对我有用,但我不需要使用`.isEnabled`行. (8认同)
  • 不幸的是,如果您在推送动画期间或在根视图控制器上滑动时,这将会冻结.我在这里发布了一个修复版本:http://stackoverflow.com/a/43433530/308315 (3认同)

Mr.*_*ean 12

这是在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
} }
Run Code Online (Sandbox Code Playgroud)

对于中间屏幕[您要在其中启用滑动手势]:

class FriendListViewController : UIViewController {

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

    super.viewDidLoad()
    swipeToPop()
}

func swipeToPop() {

    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true;
    self.navigationController?.interactivePopGestureRecognizer?.delegate = nil;
} }
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,如果您在推送动画期间或在根视图控制器上滑动时,这将会冻结.我在这里发布了一个修复版本:http://stackoverflow.com/a/43433530/308315 (2认同)

Bur*_*uay 11

我不需要为它添加gestureRecognizer 函数。我在 viewDidLoad 中添加以下代码块就足够了:

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
    self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
}
Run Code Online (Sandbox Code Playgroud)


Jos*_*nor 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)
}
Run Code Online (Sandbox Code Playgroud)

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

Cra*_*007 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
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码有助于快速向左返回以前的控制器,如 Facebook、Twitter。


And*_*huk 6

如果您希望在您的应用程序中随处可见这种行为,并且不想向个人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
    }

}
Run Code Online (Sandbox Code Playgroud)

现在,无论何时使用,QFNavigationController您都会获得所需的体验。


bud*_*ino 5

我创建了以下Swift 5+ UIViewController扩展,以便更轻松地在您需要的每个屏幕上添加/删除交互式弹出手势。

笔记:

  • 添加enableInteractivePopGesture()到每个具有自定义后退按钮的屏幕上

  • 添加导航控制器的根屏幕disableInteractivePopGesture()viewDidAppear以防止向后滑动问题这里提到的一些答案

  • disableInteractivePopGesture()可以在您不希望有后退按钮和向后滑动手势的推送屏幕上添加

    extension UIViewController: UIGestureRecognizerDelegate {
    
      func disableInteractivePopGesture() {
        navigationItem.hidesBackButton = true
        navigationController?.interactivePopGestureRecognizer?.delegate = self
        navigationController?.interactivePopGestureRecognizer?.isEnabled = false
      }
    
      func enableInteractivePopGesture() {
        navigationController?.interactivePopGestureRecognizer?.delegate = self
        navigationController?.interactivePopGestureRecognizer?.isEnabled = true
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)