滑动返回
导航控制器默认是有滑动返回功能的,当触摸屏幕的最左边部分慢慢向右移动,但是如果导航条中已经有了返回按钮,那么系统就不在支持滑动返回功能了,不支持的实际原因是处理滑动返回的手势所对应的代理delegate被设置为nil了,即手势触发了却没有调用相应的代理了,所以滑动返回功能没有实现。如果即想要导航条上的返回又想要滑动返回,就需要给delegate赋值了。
导航控制器上有一个滑动返回的手势,实际的类型为UIScreenEdgePanGestureRecognizer继承自UIGestureRecognizer
@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer;
屏幕边缘滑动手势:UIScreenEdgePanGestureRecognizer,该手势有一个属性edges用于控制在屏幕的哪块区域内能触发到该手势,系统给出选项有 视图的顶端区域、最左边、最下边、最右边,左边上边下边右边都可以唯独中间的一大块区域不行,还有一个选项是没有区域,也就是说该值是不会处理滑动后的操作的。
typedef NS_OPTIONS(NSUInteger, UIRectEdge) {
UIRectEdgeNone = 0,
UIRectEdgeTop = 1 << 0,
UIRectEdgeLeft = 1 << 1,
UIRectEdgeBottom = 1 << 2,
UIRectEdgeRight = 1 << 3,
UIRectEdgeAll = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight
}
解决方案:
@interface BWNavigationController () <UIGestureRecognizerDelegate>
@end
@implementation BWNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@", self.interactivePopGestureRecognizer);
self.interactivePopGestureRecognizer.delegate = self; // 设置代理
}
// 是否允许触发手势,如果是根视图控制器则不需要
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return self.childViewControllers.count > 1;
}
// 统一添加返回按钮
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
NSInteger count = self.childViewControllers.count;
if (count > 0) {
viewController.navigationItem.leftBarButtonItem = [UIBarButtonItem backBarButtonItemWithImage:[UIImage imageNamed:@"navigationButtonReturn"] highlightImage:[UIImage imageNamed:@"navigationButtonReturnClick"] tagert:self action:@selector(back) title:@"返回"];
}
[super pushViewController:viewController animated:animated];
}
- (void)back {
[self popViewControllerAnimated:YES];
}
@end
上述代码体验上有一点小问题,从UIScreenEdgePanGestureRecognizer.edges的选项可值,是不能做到滑动屏幕的任意区域就能实现滑动返回效果的,即全屏滑动返回效果。我们知道UIPanGestureRecognizer手势是针对整个视图的,所以我们可以自定义一个UIPanGestureRecognizer手势,添加到导航控制器的view上(因为UIScreenEdgePanGestureRecognizer手势也是添加到导航控制的view上的),通过打印手势获得的结果是 UIScreenEdgePanGestureRecognizer: 0x7fb3ba619270; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fb3ba619590>)>;
我们从结果可以知道处理滑动手势的操作的对象是target=<_UINavigationInteractiveTransition 0x7fb3ba619590>)>,方法是:action=handleNavigationTransition:,这样我们自定义的UIPanGestureRecognizer的target和action直接使用这两个值即可,因为只有系统知道该怎么处理滑动返回功能,同时我们还要给自定义的手势指定代理需要处理根视图控制器不允许滑动返回功能,既然我们添加了我们的手势了;,系统自带的滑动手势就应该禁止掉了enabled = NO,这些所有操作都在viewDidLoad方法中实现。
全屏滑动返回
#import "BWNavigationController.h"
#import "UIBarButtonItem+Item.h"
@interface BWNavigationController () <UIGestureRecognizerDelegate>
@end
@implementation BWNavigationController
+ (void)load {
[super load];
UINavigationBar *navigationBar = [UINavigationBar appearanceWhenContainedIn:self, nil];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSFontAttributeName] = [UIFont boldSystemFontOfSize:20];
[navigationBar setTitleTextAttributes:dict];
[navigationBar setBackgroundImage:[UIImage imageNamed:@"navigationbarBackgroundWhite"] forBarMetrics:UIBarMetricsDefault];
}
- (void)viewDidLoad {
[super viewDidLoad];
UIScreenEdgePanGestureRecognizer *edgePanGestureRecognizer = (UIScreenEdgePanGestureRecognizer *)self.interactivePopGestureRecognizer;
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:edgePanGestureRecognizer.delegate action:@selector(handleNavigationTransition:)];
panGestureRecognizer.delegate = self;
[self.view addGestureRecognizer:panGestureRecognizer];
edgePanGestureRecognizer.enabled = NO;
}
// 是否允许除法手势,如果是根视图控制器则不需要
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
return self.childViewControllers.count > 1;
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
// 统一设置返回按钮
NSInteger count = self.childViewControllers.count;
if (count > 0) {
viewController.navigationItem.leftBarButtonItem = [UIBarButtonItem backBarButtonItemWithImage:[UIImage imageNamed:@"navigationButtonReturn"] highlightImage:[UIImage imageNamed:@"navigationButtonReturnClick"] tagert:self action:@selector(back) title:@"返回"];
}
[super pushViewController:viewController animated:animated];
}
- (void)back {
[self popViewControllerAnimated:YES];
}
@end