问题:如果我们在项目中的返回按钮用的是
leftBarButtonItem
那么系统自带导航控制器的滑动返回功能失效了。所以用户体验很不好。
1.首先让导航控制器的滑动返回功能恢复。
滑动返回是导航控制器的功能,想到滑动返回,就应该想到手势Gesture.于是我们在导航控制器UINavigationController中去搜索有关手势的关键字Gesture会发现一个属性interactivePopGestureRecognizer。看看官方文档对它的解释吧
翻译一下啊😄这个手势是用来将栈顶控制器移除导航栈的.
导航控制器给它的view添加了这个手势, 用它来将栈顶的控制器移出导航栈.你可以使用这个属性来获取到这个手势对象,也可以将这个手势和你用户界面中的其他手势绑定在一起使用.当你将这些手势混合使用的时候,记得要调用手势的代理方法来允许可以识别多个手势来保证你的手势可以执行它指定的方法.
结论:这个手势实现的滑动返回功能。
当自定义了返回按钮之后,在viewDidLoad中来打印这个属性来看一看,这个手势是否还存在。
<UIScreenEdgePanGestureRecognizer: 0x7ff290717eb0; state = Possible; delaysTouchesBegan = YES;
view = <UILayoutContainerView 0x7ff29051d3e0>;
target= <(action=handleNavigationTransition:,
target=<_UINavigationInteractiveTransition 0x7ff290712d00>)>>
- 手势还存在。那么为什么手势却不识别呢? 这么一想,就想到了手势的代理.手势的代理属性可以监听手势的触发,也可以用来控制是否识别手势的行为。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
就是这个代理方法在捣鬼。如果使用的返回按钮不是系统自带的类型,那么这个代理方法中就会返回NO.所以手势就不起作用了。
所以把这个手势的代理赋值为nil,就不会调用代理方法了。
self.interactivePopGestureRecognizer.delegate = nil;
虽然实现了手势功能的恢复但是有个问题,当界面处于跟控制器的时候,也有可能会触发滑动返回的手势, 也会调用栈顶控制器的popViewController来将当前的控制器移出栈,由于该控制器为导航控制器的根控制器,所以就会发生异常。
所以解决问题的关键还得知道当前的栈顶控制器是不是根控制器,如果是根控制器,返回手势是不能起作用的。所以把interactivePopGestureRecognizer的代理设置为self也就是导航控制器。最终的代码如下所示:
//
// MyNavigationController.m
// OC_test
//
// Created by wyb on 2017/2/19.
// Copyright © 2017年 wyb. All rights reserved.
//
#import "MyNavigationController.h"
@interface MyNavigationController ()<UIGestureRecognizerDelegate>
@end
@implementation MyNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
self.interactivePopGestureRecognizer.delegate = self;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
//如果是根控制器那么就返回no。
return self.childViewControllers.count > 1;
}
@end
- 到此为止导航控制器的系统的滑动返回功能恢复了。不知道看懂没有啊😄😄😄。
2.如何实现全屏滑动返回功能。
- 还是分析上面打印的那个系统侧滑的手势interactivePopGestureRecognizer。请看打印信息:
<UIScreenEdgePanGestureRecognizer: 0x7ff290717eb0; state = Possible; delaysTouchesBegan = YES;
view = <UILayoutContainerView 0x7ff29051d3e0>;
target= <(action=handleNavigationTransition:,
target=<_UINavigationInteractiveTransition 0x7ff290712d00>)>>
UIScreenEdgePanGestureRecognizer
就是只有从屏幕的边缘滑动才能触发的手势。action=handleNavigationTransition:这个
handleNavigationTransition:
方法就是让子控制器出栈的方法。target=_UINavigationInteractiveTransition ,这个对象
_UINavigationInteractiveTransition
里有handleNavigationTransition:
这个方法。其实也就是你的侧滑手势调用了
_UINavigationInteractiveTransition
对象的handleNavigationTransition:
方法。重点来了,如果我们用全屏手势
UIPanGestureRecognizer
去调用_UINavigationInteractiveTransition
对象的handleNavigationTransition:
方法。是不是可以实现全屏滑动返回功能。答案是yes。😄。只要获取了
_UINavigationInteractiveTransition
对象事情就好办了。但是它是苹果的私有类。没法获取呀。但是通过摸索发现
self.interactivePopGestureRecognizer.delegate
导航控制器这个手势的默认代理属性就是一个_UINavigationInteractiveTransition这个类的对象.来看一下它的输出信息:
<_UINavigationInteractiveTransition: 0x7f9fd8d1f9d0>
前期准备工作做好了吧。那么就上最终的实现代码呗。
//
// MyNavigationController.h
// OC_test
//
// Created by wyb on 2017/2/19.
// Copyright © 2017年 wyb. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MyNavigationController : UINavigationController
//在项目中有时候咱们的全屏手势会与别的手势发生冲突,这是用来
//控制全屏手势的触发与否的。
@property(nonatomic,assign)BOOL enableBack;
@end
//
// MyNavigationController.m
// OC_test
//
// Created by wyb on 2017/2/19.
// Copyright © 2017年 wyb. All rights reserved.
//
#import "MyNavigationController.h"
@interface MyNavigationController ()<UIGestureRecognizerDelegate>
@property(nonatomic,strong)UIPanGestureRecognizer *panGesture;
@end
@implementation MyNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
//默认允许全屏手势触发。
self.enableBack = YES;
//先把系统的返回手势静止了。
self.interactivePopGestureRecognizer.enabled = NO;
//获取到_UINavigationInteractiveTransition对象。
id target = self.interactivePopGestureRecognizer.delegate;
//创建panGesture对象,添加添加target对象_UINavigationInteractiveTransition,添加事件handleNavigationTransition:。
self.panGesture = [[UIPanGestureRecognizer alloc]initWithTarget:target action:@selector(handleNavigationTransition:)];
//把panGesture的代理给导航控制器。
self.panGesture.delegate = self;
//给导航控制器的view添加全屏手势。
[self.view addGestureRecognizer:self.panGesture];
}
#pragma mark - pan手势的代理方法
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
//如果是子控制器,而且enableBack = YES,全屏手势才触发。
if (self.childViewControllers.count>1 && self.enableBack == YES) {
return YES;
}
return NO;
}
@end