都知道到导航栏自定义的时候,系统的全屏返回手势可能会失效。对于一个产品级的应用程序,没有了全屏返回手势,会是个很致命的缺陷。之前解决这个问题一般都是使用FDFullScreenPopGesture这个第三方,使用起来也非常简单,仅仅将文件拖到项目中就可以。其实吧,这个第三方和我这篇文章要将的东西,其实是一个原理。同样仅仅只是将文件拖动到工程中就可以实现全屏返回手势,仅仅80多行代码就可以实现。源码连接:https://github.com/ZhengYaWei1992/ZWFullScreenPanBackDemo
在开始这篇文章之前,首先对runtime常用的几个场景必须要知道:1、使用runtime关联属性,简化代码,减少耦合。2、使用runtime的交叉方法,替换系统方法,满足自己更多的需求。3、使用runtime获取对象的属性列表,并基于KVC设置属性值,第三方模型转换一般都会使用到这点,这是字典转模型的基础。关于上述三点,之后我会写一些文章,系统的整理下,不仅仅只是说一些简单的语法问题,还会告诉大家如何在实际项目中使用。基于runtime拦截全屏返回手势,这里我们只是用到前两点,获取属性列表这一点暂时用不到。
看一下八十多行代码如何拦截系统全屏返回手势,为所有push界面增加全屏返回手势。
首先创建一个UINavigationController的分类(UINavigationController+ZWPanBack),然后在.h文件中借助关联对象增加一个全屏拖拽返回手势。
@interface UINavigationController (ZWPanBack)
// 自定义全屏拖拽返回手势
@property (nonatomic, strong, readonly) UIPanGestureRecognizer *zw_popGestureRecognizer;
@end
在.m文件中我们还需要增加一个分类ZWFullScreenPopGestureRecognizerDelegate。其中gestureRecognizerShouldBegin:是手势的代理方法,这个方法我们可以控制全屏返回手势的开启和关闭。但是如何拦截到系统的全屏返回手势,并叫系统的管理权限移交到这段代码中,具体请往下看,设置runtime的另外一个知识点,交叉方法。另外,navigationController这个自定义属性同样也需要通过关联属性添加。
@interface ZWFullScreenPopGestureRecognizerDelegate: NSObject <UIGestureRecognizerDelegate>
@property (nonatomic, weak) UINavigationController *navigationController;
@end
@implementation ZWFullScreenPopGestureRecognizerDelegate
//代理方法的实现
//在这里做手势识别判断
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
//判断是否是根控制器,如果是直接取消手势
if (self.navigationController.viewControllers.count <= 1) {
return NO;
}
//如果正在转场动画,取消手势
if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {
return NO;
}
//判断手势方向,如果是从左往右拖动才开启手势
CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
if (translation.x <= 0) {
return NO;
}
return YES;
}
@end
在刚加载这个分类的时候,系统会调用load这个方法,这个方法中我们可以替换系统的方法,实现自定义方法。将系统方法的管理权限交给zw_pushViewController:animated:这个自定义方法。
//系统方法,加载时调用
+ (void)load {
Method originalMethod = class_getInstanceMethod([self class], @selector(pushViewController:animated:));
Method swizzledMethod = class_getInstanceMethod([self class], @selector(zw_pushViewController:animated:));
//系统方法和自定义方法交换
method_exchangeImplementations(originalMethod, swizzledMethod);
}
zw_pushViewController:animated:这个自定义方法的实现。这个方法中我们可以寻找交互手势,如果交互手势没有被添加,就添加自定义交互手势zw_popGestureRecognizer。同时禁用系统的交互手势。
//自定义方法和系统方法做交换
- (void)zw_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
//寻找交互手势,如果交互手势没有被添加就添加交互手势。
if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.zw_popGestureRecognizer]) {
[self.interactivePopGestureRecognizer.view addGestureRecognizer:self.zw_popGestureRecognizer];
NSArray *targets = [self.interactivePopGestureRecognizer valueForKey:@"targets"];
id internalTarget = [targets.firstObject valueForKey:@"target"];
SEL internalAction = NSSelectorFromString(@"handleNavigationTransition:");
self.zw_popGestureRecognizer.delegate = [self zw_fullScreenPopGestureRecognizerDelegate];
[self.zw_popGestureRecognizer addTarget:internalTarget action:internalAction];
// 禁用系统的交互手势
self.interactivePopGestureRecognizer.enabled = NO;
}
if (![self.viewControllers containsObject:viewController]) {
[self zw_pushViewController:viewController animated:animated];
}
}
就是这些,什么第三方无非也就是如此,80行代码就可以解决的事,使用起来仅仅将这个分类添加到工程中即可,因为这是基于运行时,在运行时面前所谓的私有全是暴露的。为了大家的方便就直接附上源码链接:https://github.com/ZhengYaWei1992/ZWFullScreenPanBackDemo