在UINavigationController中隐藏导航栏时不会滑回

我喜欢从UINavigationController中embedded视图inheritance的swipe包。 不幸的是,我似乎无法find一种方法来隐藏导航栏,但仍然有触摸平移轻扫手势。 我可以编写自定义手势,但是我宁愿不要依赖于UINavigationController反滑动手势。

如果我在故事板中取消选中,则后滑动不起作用

在这里输入图像说明

或者如果我以编程方式隐藏它,相同的情况。

- (void)viewDidLoad { [super viewDidLoad]; [self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES } 

有没有办法隐藏顶部导航栏,仍然有刷卡?

正在工作的黑客是将interactivePopGestureRecognizerUINavigationController的委托设置为这样:

 [self.navigationController.interactivePopGestureRecognizer setDelegate:nil]; 

但在某些情况下,它可能会产生奇怪的效果。

在我的情况下,以防止奇怪的影响

根视图控制器

 override func viewDidLoad() { super.viewDidLoad() // Enable swipe back when no navigation bar navigationController?.interactivePopGestureRecognizer?.delegate = self } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if(navigationController!.viewControllers.count > 1){ return true } return false } 

http://www.gampood.com/pop-viewcontroller-with-out-navigation-bar/

其他方法的问题

设置interactivePopGestureRecognizer.delegate = nil具有意想不到的副作用。

设置navigationController?.navigationBar.hidden = true不起作用,但不允许在导航栏中的更改隐藏。

最后,创build导航控制器的UIGestureRecognizerDelegate模型对象通常会更好。 将其设置到UINavigationController堆栈中的控制器是什么导致EXC_BAD_ACCESS错误。

完整解决scheme

首先,将这个类添加到您的项目中:

 class InteractivePopRecognizer: NSObject, UIGestureRecognizerDelegate { var navigationController: UINavigationController init(controller: UINavigationController) { self.navigationController = controller } func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return navigationController.viewControllers.count > 1 } // This is necessary because without it, subviews of your top controller can // cancel out your gesture recognizer on the edge. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } } 

然后,将导航控制器的interactivePopGestureRecognizer.delegate设置为新的InteractivePopRecognizer类的实例。

 var popRecognizer: InteractivePopRecognizer? override func viewDidLoad() { super.viewDidLoad() setInteractiveRecognizer() } private func setInteractiveRecognizer() { guard let controller = navigationController else { return } popRecognizer = InteractivePopRecognizer(controller: controller) controller.interactivePopGestureRecognizer?.delegate = popRecognizer } 

享受一个没有副作用的隐藏的导航栏,即使您的顶级控制器有表,集合,或滚动视图子视图工程。

你可以inheritanceUINavigationController,如下所示:

 @interface CustomNavigationController : UINavigationController<UIGestureRecognizerDelegate> @end 

执行:

 @implementation CustomNavigationController - (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated { [super setNavigationBarHidden:hidden animated:animated]; self.interactivePopGestureRecognizer.delegate = self; } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (self.viewControllers.count > 1) { return YES; } return NO; } @end 

从Hunter Maximillion Monk的回答开始 ,我为UINavigationController创build了一个子类,然后在我的故事板中为我的UINavigationController设置了自定义类。 这两个类的最终代码如下所示:

InteractivePopRecognizer:

 class InteractivePopRecognizer: NSObject { // MARK: - Properties fileprivate weak var navigationController: UINavigationController? // MARK: - Init init(controller: UINavigationController) { self.navigationController = controller super.init() self.navigationController?.interactivePopGestureRecognizer?.delegate = self } } extension InteractivePopRecognizer: UIGestureRecognizerDelegate { func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return (navigationController?.viewControllers.count ?? 0) > 1 } // This is necessary because without it, subviews of your top controller can cancel out your gesture recognizer on the edge. func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true } } 

HiddenNavBarNavigationController:

 class HiddenNavBarNavigationController: UINavigationController { // MARK: - Properties private var popRecognizer: InteractivePopRecognizer? // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() setupPopRecognizer() } // MARK: - Setup private func setupPopRecognizer() { popRecognizer = InteractivePopRecognizer(controller: self) } } 

故事板:

故事板导航控制器自定义类

我发现其他公布解决scheme覆盖委托,或将其设置为零导致一些意想不到的行为。

在我的情况下,当我在导航堆栈的顶部,并尝试使用手势再popup一个,它会失败(按预期),但随后的尝试推入堆栈将开始导致奇怪的graphics故障导航栏。 这是有道理的,因为委托不仅用于处理隐藏导航栏时是否阻止手势被识别,而且所有其他行为都被抛出。

从我的testing中看来, gestureRecognizer(_:, shouldReceiveTouch:)是原始gestureRecognizer(_:, shouldReceiveTouch:)的方法,用于在导航栏隐藏时阻止识别手势,而不是gestureRecognizerShouldBegin(_:) 。 在其委托工作中实现gestureRecognizerShouldBegin(_:)其他解决scheme因为缺lessgestureRecognizer(_:, shouldReceiveTouch:)将导致接收所有触摸的默认行为。

@Nathan Perry的解决scheme变得非常紧密,但是如果没有执行respondsToSelector(_:) ,向forwardingTargetForSelector(_:)发送消息的UIKit代码将认为没有任何其他委托方法的实现,并且forwardingTargetForSelector(_:)将永远不会获得调用。

因此,我们需要控制`gestureRecognizer(_ :, shouldReceiveTouch :)在一个特定的场景中我们要修改的行为,否则转发一切其他的委托。

 import Foundation class AlwaysPoppableNavigationController : UINavigationController { private var alwaysPoppableDelegate: AlwaysPoppableDelegate! override func viewDidLoad() { super.viewDidLoad() self.alwaysPoppableDelegate = AlwaysPoppableDelegate(navigationController: self, originalDelegate: self.interactivePopGestureRecognizer!.delegate!) self.interactivePopGestureRecognizer!.delegate = self.alwaysPoppableDelegate } } private class AlwaysPoppableDelegate : NSObject, UIGestureRecognizerDelegate { weak var navigationController: AlwaysPoppableNavigationController? var originalDelegate: UIGestureRecognizerDelegate init(navigationController: AlwaysPoppableNavigationController, originalDelegate: UIGestureRecognizerDelegate) { self.navigationController = navigationController self.originalDelegate = originalDelegate } @objc func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if let nav = navigationController, nav.isNavigationBarHidden && nav.viewControllers.count > 1 { { return true } else { return self.originalDelegate.gestureRecognizer!(gestureRecognizer, shouldReceive: touch) } } override func responds(to aSelector: Selector) -> Bool { if aSelector == #selector(gestureRecognizer(_:shouldReceive:)) { return true } else { return self.originalDelegate.responds(to: aSelector) } } override func forwardingTarget(for aSelector: Selector!) -> Any? { return self.originalDelegate } } 

看起来像@ChrisVasseli提供的解决scheme是最好的。 我想在Objective-C中提供相同的解决scheme,因为问题是关于Objective-C(请参阅标签)

 @interface InteractivePopGestureDelegate : NSObject <UIGestureRecognizerDelegate> @property (nonatomic, weak) UINavigationController *navigationController; @property (nonatomic, weak) id<UIGestureRecognizerDelegate> originalDelegate; @end @implementation InteractivePopGestureDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { if (self.navigationController.navigationBarHidden && self.navigationController.viewControllers.count > 1) { return YES; } else { return [self.originalDelegate gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch]; } } - (BOOL)respondsToSelector:(SEL)aSelector { if (aSelector == @selector(gestureRecognizer:shouldReceiveTouch:)) { return YES; } else { return [self.originalDelegate respondsToSelector:aSelector]; } } - (id)forwardingTargetForSelector:(SEL)aSelector { return self.originalDelegate; } @end @interface NavigationController () @property (nonatomic) InteractivePopGestureDelegate *interactivePopGestureDelegate; @end @implementation NavigationController - (void)viewDidLoad { [super viewDidLoad]; self.interactivePopGestureDelegate = [InteractivePopGestureDelegate new]; self.interactivePopGestureDelegate.navigationController = self; self.interactivePopGestureDelegate.originalDelegate = self.interactivePopGestureRecognizer.delegate; self.interactivePopGestureRecognizer.delegate = self.interactivePopGestureDelegate; } @end 

您可以使用代理委托来完成。 在构build导航控制器时,请抓住现有的代理。 并将其传入代理。 然后将所有委托方法传递给除了gestureRecognizer:shouldReceiveTouch:之外的现有委托gestureRecognizer:shouldReceiveTouch: using forwardingTargetForSelector:

build立:

 let vc = UIViewController(nibName: nil, bundle: nil) let navVC = UINavigationController(rootViewController: vc) let bridgingDelegate = ProxyDelegate() bridgingDelegate.existingDelegate = navVC.interactivePopGestureRecognizer?.delegate navVC.interactivePopGestureRecognizer?.delegate = bridgingDelegate 

代理代表:

 class ProxyDelegate: NSObject, UIGestureRecognizerDelegate { var existingDelegate: UIGestureRecognizerDelegate? = nil override func forwardingTargetForSelector(aSelector: Selector) -> AnyObject? { return existingDelegate } func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool { return true } } 

Xamarin答案:

在ViewController的类定义中实现IUIGestureRecognizerDelegate接口:

 public partial class myViewController : UIViewController, IUIGestureRecognizerDelegate 

在你的ViewController中添加下面的方法:

 [Export("gestureRecognizerShouldBegin:")] public bool ShouldBegin(UIGestureRecognizer recognizer) { if (recognizer is UIScreenEdgePanGestureRecognizer && NavigationController.ViewControllers.Length == 1) { return false; } return true; } 

在您的ViewController的ViewDidLoad()添加下面一行:

 NavigationController.InteractivePopGestureRecognizer.Delegate = this; 

我已经试过这个,它的工作完美: 如何隐藏导航栏不失去滑动能力

这个想法是在您的.h文件中实现“UIGestureRecognizerDelegate”,并将其添加到.m文件中。

 - (void)viewWillAppear:(BOOL)animated { // hide nav bar [[self navigationController] setNavigationBarHidden:YES animated:YES]; // enable slide-back if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = YES; self.navigationController.interactivePopGestureRecognizer.delegate = self; } } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return YES; } 

有些人通过用animationYES调用setNavigationBarHidden方法取得了成功。

在我没有导航栏我使用的视图控制器

 open override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) CATransaction.begin() UIView.animate(withDuration: 0.25, animations: { [weak self] in self?.navigationController?.navigationBar.alpha = 0.01 }) CATransaction.commit() } open override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) CATransaction.begin() UIView.animate(withDuration: 0.25, animations: { [weak self] in self?.navigationController?.navigationBar.alpha = 1.0 }) CATransaction.commit() } 

在互动解雇期间,后退button将会闪耀,这就是为什么我把它藏起来的原因。

当用户滑出ViewController时,如何禁用de手势识别器。 您可以将其粘贴到viewWillAppear()或ViewDidLoad()方法上。

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