zoukankan      html  css  js  c++  java
  • 仿抖音评论手势拖拽 iOS

     

    通过观察可以发现抖音的评论视图是从底部弹出的,包括顶部视图和UITableView视图,

    1.经过分析可以知道评论视图最底部是一个透明的UIView,并且添加了手势UIPanGestureRecognizer。

    2.当UITableView滑动到最顶部时,下拉白色背景视图可以滑动消失

    3.当UITableView滑动时,白色背景视图不滑动

    4.当手指点击或者拖动上面的空白区域或关闭按钮,视图消失

    下面来说说具体的实现:

    1.初始化视图并添加手势,需要遵循代理 UIGestureRecognizerDelegate

    @property (nonatomic, strong) UIView *bgView;//白色背景视图
    - (instancetype)initWithFrame:(CGRect)frame {
        if (self = [super initWithFrame:frame]) {
            self.initOriginY = 100;
            self.needReplaceY = 120;
            self.backgroundColor = [UIColor clearColor];
            
            self.bgView = [[UIView alloc] initWithFrame:CGRectMake(0, self.initOriginY, UIScreenWidth, UIScreenHeight - self.initOriginY)];
            self.bgView.backgroundColor = [UIColor whiteColor];
            [self.bgView setCorner:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadius:7];
            [self addSubview:self.bgView];//添加手势
            UIPanGestureRecognizer *panGesture = [UIPanGestureRecognizer new];
            panGesture.delegate = self;
            //cancelsTouchesInView 默认 YES,自定义的手势响应后,系统手势不再响应,但自定义手势识别前,会先执行系统手势。设置为 NO 后,自定义手势和系统手势会同时识别响应。
            panGesture.cancelsTouchesInView = NO;
            [panGesture addTarget:self action:@selector(panGestureAction:)];
            [self addGestureRecognizer:panGesture];
        }
        return self;
    }

    将列表加到白色背景视图上,我这儿用的是 UICollectionView,因为列表是瀑布流 ,如果您们用的是UITableView,作相应的变化即可。

    @property (nonatomic, strong) UICollectionView *collectionView;
    [self.bgView addSubview:self.collectionView];

    2.手势代理处理

    @property (nonatomic, assign) CGFloat beginContentOffsetY;//self.collectionView 偏移的y坐标
    @property (nonatomic, assign) CGFloat lastTouchPointY;//记录一下开始拖动时的y坐标
    @property (nonatomic, assign) BOOL isUpPan;//是否上拉
    @property (nonatomic, assign) CGFloat initOriginY;//白色背景视图顶部y坐标
    @property (nonatomic, assign) CGFloat needReplaceY;//拖动距离超过此距离时才识别手势
    - (void)panGestureAction:(UIPanGestureRecognizer *)gestureRecognizer {
        CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
        
        //self.collectionView拖动时
        if (self.collectionView.panGestureRecognizer.state != UIGestureRecognizerStatePossible) {
            //self.collectionView拖动时如果 contentOffset.y > 0 就不走自定义手势
            if (self.collectionView.contentOffset.y > 0) {
                return;
            }
        }
    
        if (translation.y > self.beginContentOffsetY) {
            self.bgView.top = translation.y - self.beginContentOffsetY  + self.initOriginY;
        } else {
            self.bgView.top = self.initOriginY;
        }
        
        if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
            //是否上拉
            self.isUpPan = ([gestureRecognizer locationInView:self].y - self.lastTouchPointY < 0 && fabs([gestureRecognizer velocityInView:self].y) > 10);
            if (translation.y > self.needReplaceY && !self.isUpPan) {
                //大于需求拖动的距离并且是下拉才取消视图
                [self dissmis];
            } else {
                //复位
                [UIView animateWithDuration:0.2f animations:^{
                    self.bgView.top = self.initOriginY;
                }];
            }
        } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
            //
            self.beginContentOffsetY = self.collectionView.contentOffset.y;
            //记录一下开始拖动时的y坐标
            self.lastTouchPointY = [gestureRecognizer locationInView:self].y;
        }
    }
    
    - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        [super touchesEnded:touches withEvent:event];
        UITouch * aTouch = touches.anyObject;
        CGPoint aPoint = [aTouch locationInView:self];
        //点击透明背景取消视图
        if (aPoint.y < self.initOriginY) {
            [self dissmis];
        }
    }

    3.展示、取消视图实现

    - (void)show {
        [UIView animateWithDuration:0.3f animations:^{
            self.top = 0;
        }];
    }
    - (void)dissmis {
        [UIView animateWithDuration:0.2f animations:^{
            self.bgView.top = UIScreenHeight;
        } completion:^(BOOL finished) {
            [self removeFromSuperview];
        }];
    }

    4.UIView的扩展设置圆角

    /// 设置圆角
    /// @param corner 圆角类型
    /// @param cornerRadius 圆角大小
    - (void)setCorner:(UIRectCorner)corner cornerRadius:(CGFloat)cornerRadius {
        CAShapeLayer *mask = [CAShapeLayer layer];
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corner cornerRadii:CGSizeMake(cornerRadius*2, cornerRadius*2)];
        mask.path = path.CGPath;
        self.layer.mask = mask;
    }

     5.调用方式

            RecommendView *recommendView = [[RecommendView alloc] initWithFrame:CGRectMake(0, UIScreenHeight, UIScreenWidth, UIScreenHeight)];
            self.view addSubview:recommendView];
            [recommendView show];

    备注:

    translationInView : 手指在视图上移动的位置(x,y)向下和向右为正,向上和向左为负。

    locationInView : 手指在视图上的位置(x,y)就是手指在视图本身坐标系的位置。

    velocityInView: 手指在视图上移动的速度(x,y), 正负也是代表方向,值得一体的是在绝对值上|x| > |y| 水平移动, |y|>|x| 竖直移动。

    在北京的灯中,有一盏是我家的。这个梦何时可以实现?哪怕微微亮。北京就像魔鬼训练营,有能力的留,没能力的走……
  • 相关阅读:
    map集合的见解、排序
    java mysql 数据类型对照
    spring 通过@Value 获取properties文件中设置了属性 ,与@Value # 和$的区别
    nginx中的超时设置,请求超时、响应等待超时等
    Linux配置多个Tomcat同时运行
    Socket TCP Server一个端口可以有多少个长连接?受到什么影响?linux最大文件句柄数量总结
    tomcat关闭后线程依然运行解决办法
    守护线程与非守护线程的区别
    SSM整合——spring4.*配置案例
    SSM事务——事务回滚如何拿到返回值
  • 原文地址:https://www.cnblogs.com/huangzs/p/15266756.html
Copyright © 2011-2022 走看看