1. 关于UINavigationController
1.1 对一个iOS开发者来说,使用系统的UINavigationController
是一件很容易的事,例如下面的代码(这里举个例子)。
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];
[self.window makeKeyAndVisible];
1.2 效果
1.3 对于简单使用很容易,但是在实际开发中,对于导航栏的定制远远不止这些例如:
- 实现侧滑效果
- 实现全屏侧滑效果
- 设置背景颜色
- 设置背景图片
- 实现导航栏透明效果
- 实现导航栏渐变效果
- 实现个别导航栏的样式
注意:对于侧滑效果来说,可以分为两种一种是融合,一种是平滑。
- 导航栏自带屏幕边缘侧滑就是融合的效果。
- 对于导航栏的平滑效果需要我们自己去实现。
2 平滑效果
2.1 平滑效果初体验
- 将父类的
navigationBarHidden
隐藏可以达到此效果
问题:
- 导航栏被隐藏了,系统的导航布局也就看不见了。
- 屏幕边缘侧滑手势也失效了。
2.2 显示布局
这里提供一种思路,还有其他方法实现效果,后面会进行介绍
思路 :虽然系统的导航控制器NAV_A(这里将被隐藏的导航控制叫做NAV_A)被隐藏了,但是其栈结构还是存在的所有被加入的子控制器依然要遵循先进后出规则。
- 由此我们如果要实现完整的导航栏的布局,必须是要有导航的支持,如果在新建一个导航NAV_B,将ViewController 子控制器添加到 NAV_B。
- 此时 NAV_A 栈中还是空的,如果将被添加的NAV_B直接作为NAV_A 的子控制器;也就是导航控制器中的子控制全部是导航控制器,这个似乎不是很合理。
- 此时如果新建一个UIViewController的 VC_TMP 控制器 将 NAV_B 作为 VC_TMP的子控制器,相当于用
UIViewController
的控制器包装 导航栏控制器。 - 最后将 VC_TMP 添加到 NAV_A 控制器中。
2.2.1 图示
- 概览
-
封装过程
2.2.2 代码演示
- 首先建立一个自控制器
MLWrapViewController : UIViewController
.h
//
// MLWrapViewController.h
// MLNavigationController
//
// Created by MrDML on 2019/6/27.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface MLWrapViewController : UIViewController
+ (instancetype)wrapWithViewController:(UIViewController *)viewController;
- (instancetype)initWithWrapViewController:(UIViewController *)viewController;
@end
NS_ASSUME_NONNULL_END
.m
//
// MLWrapViewController.m
// MLNavigationController
//
// Created by MrDML on 2019/6/27.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import "MLWrapViewController.h"
#import "MLWrapNavigationController.h"
@interface MLWrapViewController ()
@property (nonatomic, strong) MLWrapNavigationController * wrapNavigationController;
@end
@implementation MLWrapViewController
+ (instancetype)wrapWithViewController:(UIViewController *)viewController{
return [[self alloc] initWithWrapViewController:viewController];
}
- (instancetype)initWithWrapViewController:(UIViewController *)viewController{
self = [super init];
if (self) {
// 1. 初始化封装后的NavigationController
self.wrapNavigationController = [MLWrapNavigationController wrapNavigationController:viewController];
// 2. 封装后的导航栏,添加为MLWrapViewController 控制器的子控制器
[self addChildViewController:self.wrapNavigationController];
[self.wrapNavigationController didMoveToParentViewController:self];
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 添加视图
[self.view addSubview:self.wrapNavigationController.view];
// 2. 设置自动布局
self.wrapNavigationController.view.autoresizingMask= UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
// 3. 设置frame
self.wrapNavigationController.view.frame = self.view.frame;
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
- 建立被包装的导航栏
MLWrapNavigationController : UINavigationController
.h
//
// MLWrapNavigationController.h
// MLNavigationController
//
// Created by Alan.Dai on 2019/6/27.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface MLWrapNavigationController : UINavigationController
- (instancetype)initWithWrapNavigationController:(UIViewController *)viewcontroller;
+ (instancetype)wrapNavigationController:(UIViewController *)viewcontroller;
@end
NS_ASSUME_NONNULL_END
.m
//
// MLWrapNavigationController.m
// MLNavigationController
//
// Created by Alan.Dai on 2019/6/27.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import "MLWrapNavigationController.h"
#import "MLNavigationController.h"
#import "MLWrapViewController.h"
@interface MLWrapNavigationController ()
@end
@implementation MLWrapNavigationController
- (instancetype)initWithWrapNavigationController:(UIViewController *)viewcontroller{
self = [super init];
if (self) {
self.viewControllers =@[viewcontroller];
if ([viewcontroller isKindOfClass:[UITabBarController class]]) {
[self setNavigationBarHidden:YES animated:NO];
}
}
return self;
}
+ (instancetype)wrapNavigationController:(UIViewController *)viewcontroller{
return [[self alloc] initWithWrapNavigationController:viewcontroller];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
- 设置根控制器
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[MLNavigationController alloc] initWithRootViewController:[ViewController alloc]];
[self.window makeKeyAndVisible];
return YES;
}
- push 操作
- (void)click:(UIButton *)sender{
TestVC_One *oneVC = [[TestVC_One alloc] init];
NSLog(@"%@",self.navigationController); // MLWrapNavigationController
[self.navigationController pushViewController:oneVC animated:YES];
}
2.2.3 效果展示与问题
2.3 问题
- 通过上述效果可以发现以下几个问题
- 导航栏虽然出现了,但是并没有达到融合的效果。
- 屏幕边缘侧滑也可以了,但是这个和最初的效果完全一样,就是默认系统的效果
- 为什么会出现上述的问题? 我不是已经包装了吗?怎么 push之后和系统的完全一样?
2.3.1 navigationController 是谁?
- 2.3 的问题先抛开不去理会,先明确一个问题
navigationController
是谁? - 首先我们需要看 在哪里进行的push 操作
ViewController
是导航的根控制器
self.window.rootViewController = [[MLNavigationController alloc] initWithRootViewController:[ViewController alloc]];
- (void)click:(UIButton *)sender{
TestVC_One *oneVC = [[TestVC_One alloc] init];
NSLog(@"%@",self.navigationController); // MLWrapNavigationController
// 这里获取的肯定是 MLWrapNavigationController 因为 跟控制器是添加到这个导航中去的
[self.navigationController pushViewController:oneVC animated:YES];
}
通过打印
self.navigationController
我们可以知道在ViewController
中的导航栏是MLWrapNavigationController
,如果产生疑问,请看下图。-
图示
分析
viewController
被MLWrapNavigationController
管理,获取的导航栏self.navigationController
自然是MLWrapNavigationController
- 在
MLWrapNavigationController
被添作为MLWrapViewController
的子控制器 然而MLWrapViewController
是被导航MLNavigationController
管理的,那么如果在MLWrapNavigationController
中获取导航栏self.navigationController
自然是MLNavigationController
明确上面
self.navigationController
是谁后,和2.3问题 的问题有什么关系?因为我们在
viewController
中进行[self.navigationController pushViewController:oneVC animated:YES];
操作后会调用MLWrapNavigationController
中的- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
方法
这个方法是父类的,在之前的代码中我们并没有实现,覆盖系统的方法,系统方法如下
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
[super pushViewController:viewController animated:animated];
}
- 通过上述的代码我们可以看到
super
值得其实就是父类UINavigationController
- 也就是说在push操作的时候,还是走的系统的提供的默认方法 还是用
UINavigationController
进行的push- 其实我们在
MLWrapNavigationController
类中获取的导航self.navigationController
其实是MLNavigationController
MLWrapNavigationController
是被MLNavigationController
导航进行管理的。- 如果将
MLWrapNavigationController
作为一个普通的控制器看的话,那么如果在一个普通的控制器中进行push操作,
- 首先你肯定要获取导航
- 使用导航进行
push
- 通过上述分析,我们需要实现该方法,并且做出如下修改
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 获取导航栏
MLNavigationController *nav = (MLNavigationController*)self.navigationController;
// 进行push 操作
[nav pushViewController:viewController animated:animated];
}
- 效果展示
2.3.2 问题
- 上述效果展示之后,发现push操作之后,导航栏标题没出现
- 原因其实是push 的是一个普通的控制器
UIViewController
,这个肯定是行不通的。
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
MLNavigationController *nav = (MLNavigationController *)self.navigationController;
[nav pushViewController:viewController animated:animated];
}
- 我们应该push 一个
MLWrapViewController
类型的控制器 原因是因为该控制内部有一个导航栏的自控制器。 - 所以我们应该push一个带有导航栏的普通控制器。
2.3.3封装push 的子控制器
- 代码演示
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 1. 获取导航栏
MLNavigationController *nav = (MLNavigationController *)self.navigationController;
// 2. 封装普通的控制器 UIViewController -> MLWrapViewController
MLWrapViewController *wrapViewController = [MLWrapViewController wrapWithViewController:viewController];
// 3. push 操作
[nav pushViewController:wrapViewController animated:animated];
}
-
效果展示
通过改进push 操作之后导航栏标题显示正常了。
2.3.4 问题
- 系统的导航栏在push 之后无法返回。
- 边缘侧滑依然没有效果。
- 系统的导航栏在push操作之后导航的左上角有默认的返回按钮,自己实现的没有
2.3.5 分析系统的导航栏在push 之后无法返回
1. 导航栏无法返回究其原因我们在控制器TestVC_One
中
- (void)back:(UIButton *)sender{
[self.navigationController popViewControllerAnimated:YES];
}
我们知道此时的
self.navigationController
其实是MLWrapNavigationController
MLWrapNavigationController : UINavigationController
,MLWrapNavigationController继承UINavigationController
当调用
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
会执行系统默认的方法
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
[super popViewControllerAnimated:animated];
}
- 我们所看到的是点击返回之后没有效果,其实
popViewControllerAnimated
这个方法以经执行了。 - 原因是因为在
MLWrapNavigationController
只有一个控制器被压栈那就是ViewController
,根据之前封装的思路这个导航控制器中之后一个子控制器被压栈,也就是self.viewControllers =@[viewcontroller];
2 . 解决上述问题
在
MLWrapNavigationController
中的获取的导航控制器self.navigationController
其实是MLNavigationController
而不是super
修改代码
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
MLNavigationController *nav = (MLNavigationController *)self.navigationController;
return [nav popViewControllerAnimated:animated];
}
3 效果展示
4 我们知道导航栏常用的pop方式有下列三种
// 返回上一个控制器
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
MLNavigationController *nav = (MLNavigationController *)self.navigationController;
return [nav popViewControllerAnimated:animated];
}
// 返回到指定的控制器
- (NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 1.这里跳转到指定的控制器,并不是跳转到普通的控制器 UIViewController 而是需要跳转到 MLWrapViewController 类型的控制器
// 2. 所以这里需要获取 viewController 这个普通的控制器 对应的 MLWrapViewController 类型的控制器
return [self.navigationController popToViewController:? animated:animated];
}
// 返回到根控制器
- (NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated{
return [self.navigationController popToRootViewControllerAnimated:animated];
}
需要注意的是
- (NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated
这个方法其中的
viewController
并不能直接使用,而获取对应MLWrapViewController
的控制器解决方法,在本控制器中获取到
MLWrapViewController
进行保存,这里通过Runtime 动态添加属性给
UIViewContrller
添加分类UIViewController+MLNavigationController.h
.h
//
// UIViewController+MLNavigationController.h
// MLNavigationController
//
// Created by MrDML on 2019/6/28.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class MLWrapViewController;
@interface UIViewController (MLNavigationController)
/**
封装之后的控制器
*/
@property (nonatomic, strong) MLWrapViewController *wrapViewController;
@end
NS_ASSUME_NONNULL_END
.m
//
// UIViewController+MLNavigationController.m
// MLNavigationController
//
// Created by MrDML on 2019/6/28.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import "UIViewController+MLNavigationController.h"
#import <objc/Runtime.h>
@implementation UIViewController (MLNavigationController)
- (void)setWrapViewController:(MLWrapViewController *)wrapViewController{
objc_setAssociatedObject(self, @selector(wrapViewController), wrapViewController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (MLWrapViewController *)wrapViewController{
return objc_getAssociatedObject(self, @selector(wrapViewController));
}
@end
5 修改之前的代码
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 1. 获取导航栏
MLNavigationController *nav = (MLNavigationController *)self.navigationController;
// 2. 封装普通的控制器 UIViewController -> MLWrapViewController
MLWrapViewController *wrapViewController = [MLWrapViewController wrapWithViewController:viewController];
// 3. 将封装后的控制器进行保存
viewController.wrapViewController = wrapViewController;
[nav pushViewController:wrapViewController animated:animated];
}NS_ASSUME_NONNULL_END
// 返回到指定的控制器
- (NSArray<UIViewController *> *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 1.这里跳转到指定的控制器,并不是跳转到普通的控制器 UIViewController 而是需要跳转到 MLWrapViewController 类型的控制器
// 2. 所以这里需要获取 viewController 这个普通的控制器 对应的 MLWrapViewController 类型的控制器
MLWrapViewController * wrapViewController = viewController.wrapViewController;
return [self.navigationController popToViewController:wrapViewController animated:animated];
}
2.3.6 显示导航栏左上角的返回
- 实现代码
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 1. 获取导航栏
MLNavigationController *nav = (MLNavigationController *)self.navigationController;
// 2. 封装普通的控制器 UIViewController -> MLWrapViewController
MLWrapViewController *wrapViewController = [MLWrapViewController wrapWithViewController:viewController];
// 3. 将封装后的控制器进行保存
viewController.wrapViewController = wrapViewController;
// 4. 给控制器添加一个导航添加返回按钮
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(leftBack:)];
// 5. 执行push操作
[nav pushViewController:wrapViewController animated:animated];
}
- (void)leftBack:(id)button{
[self popViewControllerAnimated:YES];
}
- 通过上述代码,我们相当于把返回按钮给写死了,如果想在子控制器实现自定义的返回按钮,那么就会比较麻烦,如果我们能够提供一个可自定义的api,那么就会很灵活了,子控制器是
UIViewController
然后在这个控制器中并没有这样的接口,所以我们使用类扩展添加分类的方法解决此问题 - 给
UIViewController
添加分类
.h
//
// UIViewController+MLCategory.h
// MLNavigationController
//
// Created by MrDML on 2019/6/28.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIViewController (MLCategory)
/**
实现自定义导航栏左侧按钮
@param target target description
@param action action description
@return return value description
*/
- (UIBarButtonItem *)ml_getCustomeLeftBarButtonItemWithTarget:(id)target action:(SEL)action;
@end
NS_ASSUME_NONNULL_END
.m
//
// UIViewController+MLCategory.m
// MLNavigationController
//
// Created by MrDML on 2019/6/28.
// Copyright © 2019 top.objccn. All rights reserved.
//
#import "UIViewController+MLCategory.h"
@implementation UIViewController (MLCategory)
- (UIBarButtonItem *)ml_getCustomeLeftBarButtonItemWithTarget:(id)target action:(SEL)action{
// 提供一个默认的实现
UIButton *sender = [UIButton buttonWithType:UIButtonTypeCustom];
[sender setTitle:@"返回" forState:UIControlStateNormal];
[sender setImage:[UIImage imageNamed:@"left_back"] forState:UIControlStateNormal];
[sender addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[sender setTitleColor:[UIColor colorWithRed:18/255.0 green:150/255.0 blue:219/255.0 alpha:1] forState:UIControlStateNormal];
[sender sizeToFit];
return [[UIBarButtonItem alloc] initWithCustomView:sender];
}
@end
- 修改之前的代码
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// 1. 获取导航栏
MLNavigationController *nav = (MLNavigationController *)self.navigationController;
// 2. 封装普通的控制器 UIViewController -> MLWrapViewController
MLWrapViewController *wrapViewController = [MLWrapViewController wrapWithViewController:viewController];
// 3. 将封装后的控制器进行保存
viewController.wrapViewController = wrapViewController;
// 4. 给控制器添加一个导航添加返回按钮
if ([viewController respondsToSelector:@selector(ml_getCustomeLeftBarButtonItemWithTarget:action:)]) { // 自定义
viewController.navigationItem.leftBarButtonItem = [viewController ml_getCustomeLeftBarButtonItemWithTarget:self action:@selector(leftBack:)];
}else{ // 默认
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(leftBack:)];
}
[nav pushViewController:wrapViewController animated:animated];
}
- 在子控制器
TestVC_One
实现自定义的方法
- (UIBarButtonItem *)ml_getCustomeLeftBarButtonItemWithTarget:(id)target action:(SEL)action{
// 这里采用默认实现的
return [super ml_getCustomeLeftBarButtonItemWithTarget:target action:action];
}
2.3.7 如何在子控制器中获取根导航控制器MLNavigationController
- 在UIViewController+MLNavigationController.h 新增扩展方法
.h
/**
控制器的根导航控制器
*/
@property (nonatomic, weak, readonly) MLNavigationController *ml_navigationController;
.m
- (MLNavigationController *)ml_navigationController{
UIViewController *vc = self;
/**
vc <--> UIViewContrller ; vc.navigationController --> MLWrapNavigationController
vc <--> MLWrapNavigationController ; vc.navigationController === (导航栏控制器的导航) --> MLNavigationController
*/
while (vc && ![vc isKindOfClass:[MLNavigationController class]]) {
vc = vc.navigationController;
}
MLNavigationController *nav = (MLNavigationController *)vc;
return nav;
}
- 在子控制器中获取
NSLog(@"->%@",self.navigationController); // <MLWrapNavigationController: 0x7f943e86a200>
NSLog(@"->%@",self.navigationController.navigationController); // <MLNavigationController: 0x7f943e84f400>
// 获取根导航控制器
NSLog(@"->%@",self.ml_navigationController); // <MLNavigationController: 0x7f943e84f400>
- 阶段完成效果
2.4 实现侧滑
2.4.1 实现系统屏幕边缘侧滑
- 代码实现
- (void)viewDidLoad {
[super viewDidLoad];
[super setDelegate:self];
[super setNavigationBarHidden:YES animated:NO];
self.navigationBarHidden = YES;
// 手势识别器负责将顶视图控制器弹出导航堆栈
NSLog(@"====>%@", self.interactivePopGestureRecognizer.delegate);
/**
<UIScreenEdgePanGestureRecognizer: 0x7feb20d143a0; state = Possible; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7feb20d136c0>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7feb20d004d0>)>>
*/
// 设置手势代理
UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
// 想知道UIGestureRecognizer 的target 对象是谁
NSLog(@"gesture delegate = %@",gesture.delegate); // _UINavigationInteractiveTransition
gesture.delaysTouchesBegan = YES;
gesture.delegate = self;
gesture.enabled = YES;
}
- 效果
2.4.2 全屏自定义侧滑 -
UIScreenEdgePanGestureRecognizer
是用来处理屏幕侧滑手势的一个类 - 我们在导航栏控制器中通过获取
self.interactivePopGestureRecognizer
打印,得到如下信息。
<UIScreenEdgePanGestureRecognizer: 0x7feb20d143a0; state = Possible; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7feb20d136c0>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7feb20d004d0>)>>
- 通过打印信息我可以确定一些信息
通过
UIScreenEdgePanGestureRecognizer
可以确定是屏幕边缘侧滑手势.处理手势的方法
action
是handleNavigationTransition
target
是_UINavigationInteractiveTransition
它是接收器识别手势时发送的动作消息的接收者,也就是说handleNavigationTransition
属于_UINavigationInteractiveTransition
这个对象的方法。我们又可以知道
interactivePopGestureRecognizer
是UIGestureRecognizer
这种类型的对象继承关系
UIScreenEdgePanGestureRecognizer : UIPanGestureRecognizer
;UIPanGestureRecognizer : UIGestureRecognizer
所以我们可以获取该
UIGestureRecognizer
对象,并且获取他的delegate
发现delegat
就是_UINavigationInteractiveTransition
通过打印
gesture.view
和self.view
发现两者是同一个,就是本导航控制器的view视图
NSLog(@"gesture.view = %@",gesture.view);
// gesture.view = <UILayoutContainerView: 0x7fb6aaf0bf60; frame = (0 0; 414 896); autoresize = W+H; gestureRecognizers = <NSArray: 0x600003cb15c0>; layer = <CALayer: 0x6000032a0be0>>
NSLog(@"self.view = %@",self.view);
// self.view = <UILayoutContainerView: 0x7fb6aaf0bf60; frame = (0 0; 414 896); autoresize = W+H; gestureRecognizers = <NSArray: 0x600003cb15c0>; layer = <CALayer: 0x6000032a0be0>>
- 通过上诉的分析我们可以写出这样的代码
- (void)viewDidLoad {
[super viewDidLoad];
[super setDelegate:self];
[super setNavigationBarHidden:YES animated:NO];
self.navigationBarHidden = YES;
// 手势识别器负责将顶视图控制器弹出导航堆栈
NSLog(@"====>%@", self.interactivePopGestureRecognizer.delegate);
/**
<UIScreenEdgePanGestureRecognizer: 0x7feb20d143a0; state = Possible; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7feb20d136c0>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7feb20d004d0>)>>
*/
// 设置手势代理
UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
// 想知道UIGestureRecognizer 的target 对象是谁
NSLog(@"gesture delegate = %@",gesture.delegate); // _UINavigationInteractiveTransition
NSLog(@"gesture.view = %@",gesture.view);
// gesture.view = <UILayoutContainerView: 0x7fb6aaf0bf60; frame = (0 0; 414 896); autoresize = W+H; gestureRecognizers = <NSArray: 0x600003cb15c0>; layer = <CALayer: 0x6000032a0be0>>
NSLog(@"self.view = %@",self.view);
// self.view = <UILayoutContainerView: 0x7fb6aaf0bf60; frame = (0 0; 414 896); autoresize = W+H; gestureRecognizers = <NSArray: 0x600003cb15c0>; layer = <CALayer: 0x6000032a0be0>>
// 自定义手势
// 将手势添加到view上
UIPanGestureRecognizer *panGester = [[UIPanGestureRecognizer alloc] initWithTarget:gesture.delegate action:NSSelectorFromString(@"handleNavigationTransition:")];
// 其实就是本控制器的view视图
[gesture.view addGestureRecognizer:panGester];
gesture.delaysTouchesBegan = YES;
panGester.delegate = self;
// 一旦触发手势本控制器view的手势,就会由gesture.delegate 对象去执行handleNavigationTransition: 这个方法 完成手势的处理
NSLog(@"1.%@",self.viewControllers);
}
#pragma mark - UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
// 如果只有一个控制器,就不需要侧滑了
if(self.childViewControllers.count == 1) {
return NO;
}
return YES;
}
- 一旦触发手势本控制器
view
的手势,就会由gesture.delegate
对象去执行handleNavigationTransition:
这个方法 完成手势的处理 -
效果
总结
实现的大致思路是这样的,还有很多细节需要处理。这里就不详细书写了,这种实现思路曾在一个demo看到是这样实现的,这里整理一下。