实现全屏滑动,解决UIScrollerView的冲突

2018-02-25  本文已影响192人  874b526fa570

实现全屏滑动,解决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 文件夹下 需要的小伙伴 直接下载这个文件 然后放到自己的项目中可以了,不需要写代码

上一篇下一篇

猜你喜欢

热点阅读