实现全屏滑动,解决UIScrollerView的冲突
之前我们追求 让你三行代码, 今天我们追求让你随便写代码,
喜欢单手操作的我在这里我要隆重地赞美一下,今日头条,简书APP ,我喜欢他们的原因就是他们完美实现了全屏滑动,不像有些APP 还要点左上角或者屏幕边缘才能返回,这不符合单身20多年的我的习惯!
装逼结束,下面是正文:
今天我们要实现的是不需要写一行代码,实现全屏滑动,并且解决UIScrollerView的冲突
先看下效果
演示2.gif
最后一个界面就是不要全屏滑动的界面
创建一个 UINavigationController的分类,在分类中操作
基础知识
打印
NSLog(@"---------%@",self.navigationController.interactivePopGestureRecognizer);
得到的结果如下
<UIScreenEdgePanGestureRecognizer: 0x7f9ee0c3b5a0;
state = Possible;
delaysTouchesBegan = YES;
view = <UILayoutContainerView 0x7f9ee0c38170>;
target= <(action=handleNavigationTransition:
target=<_UINavigationInteractiveTransition 0x7f9ee0c3b460>)>>
系统管理全屏滑动的类是 UIScreenEdgePanGestureRecognizer 这个类
执行者是 _UINavigationInteractiveTransition == self.interactivePopGestureRecognizer.delegate
方法是 handleNavigationTransition
知道上面的思路我们需要做的就是 用我们自己的手势去拦截系统的手势,然后去实现系统全屏滑动的效果 方法: <handleNavigationTransition>
首先我们自定义手势 popPanGestureRecognizer
他的taget 和action 还是系统的方法,但是 手势的delegate 换成 self 就是导航栏控制器
然后通过 runtime hook 在viewWillAppear 的方法中 将自己创建的手势添加到 self.view 就是导航栏控制器
接下来需要处理手势
首先需要设置
///是否允许接收手指的触摸点
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return self.childViewControllers.count > 1; // 只有非跟控制器才需要触发手势
}
在跟控制器中不响应
同时还需要写下面的方法,处理手势冲突
/// 只有当系统侧滑手势失败了,才去触发ScrollView的滑动 这个方法返回YES,第一个和第二个互斥时,第二个会失效
/**
第一个手势是 <UIPanGestureRecognizer: 0x7fdd5d70ac90; state = Possible; view = <UILayoutContainerView 0x7fdd5d50ff20>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fdd5d40fd60>)>>
第二个手势是 <UIScrollViewPanGestureRecognizer: 0x7fdd5d425840; state = Possible; delaysTouchesEnded = NO; view = <UIScrollView 0x7fdd5d8ec000>; target= <(action=handlePan:, target=<UIScrollView 0x7fdd5d8ec000>)>>
*/
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
处理完手势冲突后 需要处理 手势的作用范围,代码如下:
///开始进行手势识别时调用的方法是否接收一个手势触摸事件默认为YES返回NO为不接收用处:可以在控件指定的位置使用手势识别
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
{
if ([[self valueForKey:@"_isTransitioning"] boolValue])
{
return NO;
}
if ([self.navigationController.transitionCoordinator isAnimated])
{
return NO;
}
if (self.childViewControllers.count <= 1)
{
return NO;
}
BOOL res = NO;
if (self.isEnabled == YES)
{
// 侧滑手势触发位置
CGPoint location = [gestureRecognizer locationInView:self.view];
CGPoint offSet = [gestureRecognizer translationInView:gestureRecognizer.view];
BOOL ret = (0 < offSet.x && location.x <= self.recognizerLength);
return ret;
}
return res;
}
如果你需要定制 在viewdidload方法中写下面的属性
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
这个属性控制着 左边滑动手势的区域
self.navigationController.recognizerLength = 100;
});
除此之外
还可以设计成某一个界面不想要全屏滑动的设计
给viewcontroller 添加一个分类 同时绑定一个属性控制可用与否
具体代码如下:
分别在 viewWillAppear 和 viewWillDisappear 对方法进行交换 和赋值
-(void)_vcViewWillAppear:(BOOL)animated
{
[self _vcViewWillAppear:animated];
if (self.noPopAction)
{
self.navigationController.popPanGestureRecognizer.enabled = NO;
}
}
-(void)_vcViewWillDisAppear:(BOOL)animated
{
[self _vcViewWillDisAppear:animated];
if (self.noPopAction)
{
self.navigationController.popPanGestureRecognizer.enabled = YES;
}
}
实际使用的过程中中在 适当位置写上下面的代码就ok
self.noPopAction = YES; // 这个界面不想要全屏手势了
到此不需要写一行代码就可以搞定全屏滑动,并且也不影响你自己的 UISCrollerView的手势
同学们如果有什么建议反馈请 简书上联系我
https://www.jianshu.com/u/874b526fa570
同学们也别忘了支持一下我写的多张照片照片选择框架
https://github.com/wangjinshan/IJSPhotoSDK
本篇文档的内容将放到
https://github.com/wangjinshan/IJSPhotoSDK 目录下 IJSNavigationBack 文件夹下 需要的小伙伴 直接下载这个文件 然后放到自己的项目中可以了,不需要写代码
网友评论