随着现在应用的UI越来越多样化,导航栏的颜色不再是单一颜色,还有些需要隐藏
navigationBar的需求,所以UINavigationController的navigationBar在不同颜色或隐藏的情况下切换效果显得不够平滑,越来越多的应用也开始使用类似网易新闻,网易云音乐,淘宝这种类似的切换效果.
一、介绍
效果的实现大致分3种方式
- 使用自定义
navigationBar.淘宝,网易新闻,达令等使用的是这种 灵活度高,实现的效果好.但是缺点也明显,自定义的navigationBar使用起来没有原生的使用起来方便.如果是已有项目要转换的话会有点麻烦. - 用截图的办法,在push到下一个页面时,截取屏幕,在使用edgePan来pop时看到的就是背后的截图,也能实现这种效果.京东,天猫等使用的是这种 实现起来相对比较简单,缺点是截图是静态的,在侧滑返回时看到的不是即时的内容.而且这种方法只是在pop时有效果,push时并没有,还是原生push的渐变效果.
- 使用了一种比较特别,比较巧妙的办法实现的,也就是网易云音乐的实现方法,后面会分析一下这种实现
集合了前两种的优点,有易集成,效果也好的特点.并且不影响原有的导航栏的
navigationItem和其他navigationBar的属性,包括设置的barTintColor,backgroundImage等.
目前存在问题:隐藏TabBar其实就是用控制器本身的NavigationController来Push就可以实现不隐藏,而不用
根控制器的NavigationController来Push。
现在就能打开看出来个大概了,看到这的时候我就知道网易云音乐原来是使用了一个比较巧的方法,简单来说这个方法具体的流程就是:
- 首先根控制器是一个导航控制器,这个导航控制器管理所有的页面的push和pop操作,并且这个导航控制器的导航栏
navigationBar是隐藏的,并且是用setNavigationBarHidden:的方式隐藏,而不是navgationBar.hidden = YES,因为setNavigationBarHidden:的方式其实是直接将navigationBar给移除了,而navgationBar.hidden = YES只是让navigationBar变透明了. - 这个根的
UINavigationControler下面还有一个UIViewController,是因为如果我们需要显示的界面本身也是一个UINavigationControler,那就会出问题了,UINavigationControler嵌套UINavigationControler的方式是不被允许的,和后面第三条相似.所以要进行包装. - 然后每当push一个
ViewControler时,给这个ViewController进行了包装,先在外面包了一个UINavigationController,然后再在UINavigationControler外包一个UIViewController,这样做的原因是UINavigationControler不能push另一个UINavigationControler,所以需要再包一层UIViewController. - 最后我们真真正正在界面上看到的导航栏就是属于这个包在
UIViewControler外的UINavigationControler的.因为每个页面都有属于自己的导航控制器,然后再有最外层的UINavigationControler来管理页面的push和pop操作,也就实现了我们想要的效果.
UIWindow
|
UINavigationController
|
UIViewController
|
UITabBarController
|
UINavigationController
|
UIViewController
|
UINavigationController
|
UIViewController
最外层的UINavigationControler是固定不变的,用来操作push和pop操作,UITabBarController的viewControllers也存放的是UINavigationControler,那么这个UINavigationControler的rootViewController也就需要进行一次UIViewController-UINavgationController的包装.也就形成了目前的这个结构.
二、分析
1、全局pop
UIPanGestureRecognizer替换原有interactivePopGestureRecognizer手势,在UINavigationControllerDelegate代理方法didShowViewController中self.view上添加手势。
#import "JTNavigationController.h"
@interface JTNavigationController ()<UINavigationControllerDelegate>
@property (nonatomic, strong) UIPanGestureRecognizer *popPanGesture;
@property (nonatomic, strong) id popGestureDelegate;
@end
@implementation JTNavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
if (self = [super initWithRootViewController:rootViewController]) {
self.viewControllers = @[rootViewController];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
self.popGestureDelegate = self.interactivePopGestureRecognizer.delegate;
SEL action = NSSelectorFromString(@"handleNavigationTransition:");
self.popPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self.popGestureDelegate action:action];
self.popPanGesture.maximumNumberOfTouches = 1;
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
BOOL isRootVC = viewController == navigationController.viewControllers.firstObject;
if (isRootVC) {
[self.view removeGestureRecognizer:self.popPanGesture];
} else {
[self.view addGestureRecognizer:self.popPanGesture];
}
}
@end
2、push和pop操作JTNavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
if (self = [super init]) {
// 指定rootViewController的导航栏为JTNavigationController
rootViewController.jt_navigationController = self;
self.viewControllers = @[[JTWrapViewController wrapViewControllerWithViewController:rootViewController]];
}
return self;
}
通过VC分类绑定属性
#import "UIViewController+JTNavigationExtension.h"
#import <objc/runtime.h>
@implementation UIViewController (JTNavigationExtension)
- (BOOL)jt_fullScreenPopGestureEnabled {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setJt_fullScreenPopGestureEnabled:(BOOL)fullScreenPopGestureEnabled {
objc_setAssociatedObject(self, @selector(jt_fullScreenPopGestureEnabled), @(fullScreenPopGestureEnabled), OBJC_ASSOCIATION_RETAIN);
}
- (JTNavigationController *)jt_navigationController {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setJt_navigationController:(JTNavigationController *)navigationController {
objc_setAssociatedObject(self, @selector(jt_navigationController), navigationController, OBJC_ASSOCIATION_ASSIGN);
}
@end