在UINavigationController中隐藏导航栏时不会滑回
我喜欢从UINavigationController中embedded视图inheritance的swipe包。 不幸的是,我似乎无法find一种方法来隐藏导航栏,但仍然有触摸平移轻扫手势。 我可以编写自定义手势,但是我宁愿不要依赖于UINavigationController反滑动手势。
如果我在故事板中取消选中,则后滑动不起作用
或者如果我以编程方式隐藏它,相同的情况。
- (void)viewDidLoad { [super viewDidLoad]; [self.navigationController setNavigationBarHidden:YES animated:NO]; // and animated:YES }
有没有办法隐藏顶部导航栏,仍然有刷卡?
正在工作的黑客是将interactivePopGestureRecognizer
的UINavigationController
的委托设置为这样:
[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; }