美文网首页
手势-高仿抖音评论效果

手势-高仿抖音评论效果

作者: 守护地中海的花 | 来源:发表于2019-06-17 16:39 被阅读0次

    要求:1.不拖拽视图 自然弹上弹下 2.拖拽视图滑动到一定位置选择回弹或销毁

    视图效果.gif

    bgScroll允许多手势

    @implementation BSTScrollView
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
        return YES;
    }
    @end
    

    核心思路:

    1.给父视图(self)添加手势。监听手势滑动范围,改变self的frame、监听手势结束状态,判断self是否销毁还是恢复原样

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panMethod:)];
        [self addGestureRecognizer:pan];
    

    2.监听父视图(self)frame 判断bgScroll是否可以滑动

    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
        //对fame添加监听
        [self addObserver:self forKeyPath:@"frame" options:options context:nil];
    

    3.当mainViewContentOffset.y 为正数 说明向上滑动 这时候禁止pan手势
    4.当监听到frame回复原样 是否要禁止mainView滑动
    5.当禁止mainView滑动,滑动mainView无效果 手势传递到父视图(self)开始下拉

    完整代码

    有个小bug 就是滚动mainView 当mainView滚动到顶部 继续往下滑 整体不会下移 因为这个时候手势没有继续传递到self
    .h

    @property(nonatomic,weak)BaseVC *parentVC;
    

    .m

    #import "BSTScrollView.h"
    //自身高度
    #define kSelfHeight (HEIGHT-kStatusBarHeight)
    //滑动限度
    #define limitValue (kSelfHeight *0.5)
    //正常的top
    #define originTop (kStatusBarHeight)
    @interface LocalMallSortCateView ()<UIScrollViewDelegate>
    @property(nonatomic,strong)UIControl *bgControl;
    @property(nonatomic,strong)UIPanGestureRecognizer *panGes;
    @property(nonatomic,strong)BSTScrollView *mainView;
    @property(nonatomic,strong)UIButton *closeButton;
    @end
    
    @implementation LocalMallSortCateView
    - (instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]) {
            [self createProperty];
            [self createUI];
            [self addPanGes];
            [self startAnimation];
        }
        return self;
    }
    - (void)createProperty
    {
        
    }
    - (void)createUI
    {
        //将self 放到bgControl 进行UI处理
        [self.bgControl addSubview:self];
        self.backgroundColor = [UIColor whiteColor];
        self.frame = CGRectMake(0, HEIGHT, WIDTH, kSelfHeight);
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(10, 0)];
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
        maskLayer.frame = self.bounds;
        maskLayer.path = maskPath.CGPath;
        self.layer.mask = maskLayer;
        //子视图
        [self mainView];
        [self closeButton];
        
        //测试 扩大mainView动范围
        [self.mainView setContentSize:CGSizeMake(0, HEIGHT + HEIGHT)];
        self.mainView.backgroundColor = [UIColor redColor];
    }
    - (void)setParentVC:(BaseVC *)parentVC
    {
        _parentVC = parentVC;
        [parentVC.view addSubview:self.bgControl];
    }
    #pragma mark - 点击事件
    - (void)clickBgControl:sender
    {
        [self endAnimation];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.21 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self removeAllSubviews];
            [self.bgControl removeAllSubviews];
            [self.bgControl removeFromSuperview];
        });
    }
    #pragma mark --------------------------手势效果--------------------------
    - (void)addPanGes
    {
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panMethod:)];
        [self addGestureRecognizer:pan];
        self.panGes = pan;
        NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
        //对fame添加监听
        [self addObserver:self forKeyPath:@"frame" options:options context:nil];
    }
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"frame"])
        {
            if (ceil(self.top) != ceil(originTop))
            {
                NSLog(@"离开顶部");
                self.mainView.scrollEnabled = NO;
            }
            else
            {
                NSLog(@"到达顶部");
                self.mainView.scrollEnabled = YES;
            }
        }
    }
    
    - (void)panMethod:(UIPanGestureRecognizer *)panGes
    {
        //translationInView:该方法返回在横坐标上、纵坐标上拖动了多少像素
        //velocityInView:在指定坐标系统中pan gesture拖动的速度
        CGPoint point = [panGes translationInView:self];
        //向下拉动 y 为正数 向上拉动 y 为负数 因为手势传递 所以mainView滑动距离
        NSLog(@"panMethod:%f",point.y);
        //0.向下滑动-改变self的frame
        if (point.y > 0)
        {
            self.frame = CGRectMake(0, originTop + point.y, WIDTH, kSelfHeight);
        }
        //2.滑动结束-判断销毁、恢复
        if (panGes.state == UIGestureRecognizerStateEnded)
        {
            NSLog(@"panGes结束");
            if (point.y >= limitValue)
            {
                [self clickBgControl:nil];
            }
            else
            {
                [self startAnimation];
            }
        }
    }
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        CGPoint point = [scrollView.panGestureRecognizer translationInView:self];
        NSLog(@"scrollViewDidScroll:%f,%f",point.y,scrollView.contentOffset.y)
        if (scrollView.contentOffset.y <= 0)
        {
            self.panGes.enabled = YES;
            [scrollView setContentOffset:CGPointZero];
            if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateEnded)
            {
                NSLog(@"scrollView滑动结束...");
                if (point.y >= limitValue)
                {
                    [self clickBgControl:nil];
                }
                else
                {
                    [self startAnimation];
                }
            }
        }
        else
        {
            if (scrollView.contentOffset.y > 10)
            {
                self.panGes.enabled = NO;
            }
            else
            {
                self.panGes.enabled = YES;
            }
        }
    }
    #pragma mark - 动画
    - (void)startAnimation
    {
        [UIView animateWithDuration:0.2 animations:^{
            self.frame = CGRectMake(0, originTop, WIDTH, kSelfHeight);
        }];
    }
    - (void)endAnimation
    {
        [UIView animateWithDuration:0.2 animations:^{
            self.frame = CGRectMake(0, HEIGHT, WIDTH, kSelfHeight);
        }];
    }
    #pragma mark - Lazy懒加载区域
    - (UIControl *)bgControl
    {
        if (!_bgControl)
        {
            UIControl *bgControl = [[UIControl alloc]initWithFrame:CGRectMake(0, 0, WIDTH, HEIGHT)];
            [sharedAppDelegate.window addSubview:bgControl];
            bgControl.backgroundColor = RGB(0, 0, 0, 0.2);
            [bgControl addTarget:self action:@selector(clickBgControl:) forControlEvents:UIControlEventTouchDown];
            _bgControl = bgControl;
        }
        return _bgControl;
    }
    - (BSTScrollView *)mainView
    {
        if (!_mainView)
        {
            BSTScrollView *scroll = [[BSTScrollView alloc]initWithFrame:CGRectMake(0, 45, WIDTH, kSelfHeight - 45)];
            [self addSubview:scroll];
            scroll.backgroundColor = [UIColor whiteColor];
            scroll.delegate = self;
            _mainView = scroll;
        }
        return _mainView;
    }
    - (UIButton *)closeButton
    {
        if (!_closeButton) {
            UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
            [self addSubview:button];
            [button setImage:[UIImage getPNGimageInBundleWithName:@"close_black"] forState:UIControlStateNormal];
            button.frame = CGRectMake(0, 0, 14+30, 14+30);
            button.imageEdgeInsets = UIEdgeInsetsMake(15, 15, 15, 15);
            [button addTarget:self action:@selector(clickBgControl:) forControlEvents:UIControlEventTouchDown];
            _closeButton = button;
        }
        return _closeButton;
    }
    - (void)dealloc
    {
        [self removeObserver:self forKeyPath:@"frame"];
        NSLog(@"LocalMallSortCateView---dealloc");
    }
    @end
    
    

    最新版本的滑动

    缺点就是滑动上来 mainView太过灵巧

    • 1.去除mainView scrollEnbale的判断
    • 2.加入了dragging来判断手指离开mainView
    #import "BSTScrollView.h"
    //自身高度
    #define kSelfHeight (HEIGHT-kStatusBarHeight)
    //滑动限度
    #define limitValue (kSelfHeight *0.5)
    //正常的top
    #define originTop (kStatusBarHeight)
    @interface LocalMallSortCateView ()<UIScrollViewDelegate>
    @property(nonatomic,strong)UIControl *bgControl;
    @property(nonatomic,strong)UIPanGestureRecognizer *panGes;
    @property(nonatomic,assign)BOOL dragging;
    @property(nonatomic,strong)BSTScrollView *mainView;
    @property(nonatomic,strong)UIButton *closeButton;
    @end
    
    @implementation LocalMallSortCateView
    - (instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]) {
            [self createProperty];
            [self createUI];
            [self addPanGes];
            [self startAnimation];
        }
        return self;
    }
    - (void)createProperty
    {
        
    }
    - (void)createUI
    {
        //将self 放到bgControl 进行UI处理
        [self.bgControl addSubview:self];
        self.backgroundColor = [UIColor whiteColor];
        self.frame = CGRectMake(0, HEIGHT, WIDTH, kSelfHeight);
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(10, 0)];
        CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
        maskLayer.frame = self.bounds;
        maskLayer.path = maskPath.CGPath;
        self.layer.mask = maskLayer;
        //子视图
        [self mainView];
        [self closeButton];
        
        //测试 扩大mainView动范围
        [self.mainView setContentSize:CGSizeMake(0, HEIGHT + HEIGHT)];
        self.mainView.backgroundColor = [UIColor redColor];
    }
    - (void)setParentVC:(BaseVC *)parentVC
    {
        _parentVC = parentVC;
        [parentVC.view addSubview:self.bgControl];
    }
    #pragma mark - 点击事件
    - (void)clickBgControl:sender
    {
        [self endAnimation];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.21 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [self removeAllSubviews];
            [self.bgControl removeAllSubviews];
            [self.bgControl removeFromSuperview];
        });
    }
    #pragma mark --------------------------手势效果--------------------------
    - (void)addPanGes
    {
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panMethod:)];
        [self addGestureRecognizer:pan];
        self.panGes = pan;
        NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
        //对fame添加监听
        [self addObserver:self forKeyPath:@"frame" options:options context:nil];
    }
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        if ([keyPath isEqualToString:@"frame"])
        {
            if (ceil(self.top) != ceil(originTop))
            {
                //NSLog(@"离开顶部");
                [self.mainView setContentOffset:CGPointZero];
            }
            else
            {
                //NSLog(@"到达顶部");
            }
        }
    }
    
    - (void)panMethod:(UIPanGestureRecognizer *)panGes
    {
        //translationInView:该方法返回在横坐标上、纵坐标上拖动了多少像素
        //velocityInView:在指定坐标系统中pan gesture拖动的速度
        CGPoint point = [panGes translationInView:self];
        //向下拉动 y 为正数 向上拉动 y 为负数 因为手势传递 所以mainView滑动距离
        //NSLog(@"panMethod:%f",point.y);
        //0.向下滑动-改变self的frame
        if (point.y > 0)
        {
            self.frame = CGRectMake(0, originTop + point.y, WIDTH, kSelfHeight);
        }
        //2.滑动结束-判断销毁、恢复
        if (panGes.state == UIGestureRecognizerStateEnded)
        {
            //NSLog(@"panGes结束");
            if (point.y >= limitValue)
            {
                [self clickBgControl:nil];
            }
            else
            {
                [self startAnimation];
            }
        }
    }
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        CGPoint point = [scrollView.panGestureRecognizer translationInView:self];
        //NSLog(@"point.y:%f scrollView.contentOffset.y:%f",point.y,scrollView.contentOffset.y)
        if (scrollView.contentOffset.y > 10)
        {
            self.panGes.enabled = NO;
        }
        else
        {
            self.panGes.enabled = YES;
        }
        
        if (scrollView.contentOffset.y <= 0)
        {
            [scrollView setContentOffset:CGPointZero];
            if (self.dragging) {
                //手势必须在
                if (ceil(self.top) != ceil(originTop))
                {
                    NSLog(@"回滑---向上");
                    [scrollView setContentOffset:CGPointZero];
                    CGFloat top = 0;
                    if ((originTop + point.y) > originTop) {
                        top = originTop + point.y;//向下
                    } else {
                        top = originTop;//不能一直向上
                    }
                    self.frame = CGRectMake(0, top, WIDTH, kSelfHeight);
                }
                //临界点
                if (ceil(self.top) == ceil(originTop)) {
                    NSLog(@"到达临界点...");
                    self.frame = CGRectMake(0, originTop + 1, WIDTH, kSelfHeight);
                }
            }
        }
        else
        {
            if (self.dragging) {
                if (ceil(self.top) != ceil(originTop))
                {
                    NSLog(@"回滑---向下");
                    [scrollView setContentOffset:CGPointZero];
                    CGFloat top = 0;
                    if (originTop + point.y > originTop) {
                        top = originTop + point.y;
                    } else {
                        top = originTop;
                    }
                    self.frame = CGRectMake(0, top, WIDTH, kSelfHeight);
                }
            }
        }
    }
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        NSLog(@"scrollView滑动结束...");
    }
    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        NSLog(@"手指开始滑动mainView");
        self.dragging = YES;
    }
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
        self.dragging = NO;
        NSLog(@"手指离开mainView");
        if (ceil(self.top) != ceil(originTop)) {
            CGPoint point = [scrollView.panGestureRecognizer translationInView:self];
            if (point.y >= limitValue)
            {
                [self clickBgControl:nil];
            }
            else
            {
                [self startAnimation];
            }
        }
    }
    #pragma mark - 动画
    - (void)startAnimation
    {
        [UIView animateWithDuration:0.2 animations:^{
            self.frame = CGRectMake(0, originTop, WIDTH, kSelfHeight);
        }];
    }
    - (void)endAnimation
    {
        [UIView animateWithDuration:0.2 animations:^{
            self.frame = CGRectMake(0, HEIGHT, WIDTH, kSelfHeight);
        }];
    }
    #pragma mark - Lazy懒加载区域
    - (UIControl *)bgControl
    {
        if (!_bgControl)
        {
            UIControl *bgControl = [[UIControl alloc]initWithFrame:CGRectMake(0, 0, WIDTH, HEIGHT)];
            [sharedAppDelegate.window addSubview:bgControl];
            bgControl.backgroundColor = RGB(0, 0, 0, 0.2);
            [bgControl addTarget:self action:@selector(clickBgControl:) forControlEvents:UIControlEventTouchDown];
            _bgControl = bgControl;
        }
        return _bgControl;
    }
    - (BSTScrollView *)mainView
    {
        if (!_mainView)
        {
            BSTScrollView *scroll = [[BSTScrollView alloc]initWithFrame:CGRectMake(0, 45, WIDTH, kSelfHeight - 45)];
            [self addSubview:scroll];
            scroll.backgroundColor = [UIColor whiteColor];
            scroll.delegate = self;
            _mainView = scroll;
        }
        return _mainView;
    }
    - (UIButton *)closeButton
    {
        if (!_closeButton) {
            UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
            [self addSubview:button];
            [button setImage:[UIImage getPNGimageInBundleWithName:@"close_black"] forState:UIControlStateNormal];
            button.frame = CGRectMake(0, 0, 14+30, 14+30);
            button.imageEdgeInsets = UIEdgeInsetsMake(15, 15, 15, 15);
            [button addTarget:self action:@selector(clickBgControl:) forControlEvents:UIControlEventTouchDown];
            _closeButton = button;
        }
        return _closeButton;
    }
    - (void)dealloc
    {
        [self removeObserver:self forKeyPath:@"frame"];
        NSLog(@"LocalMallSortCateView---dealloc");
    }
    @end
    
    

    变形版本

    1.根据版本一进行布局-百事通项目 地图项目 视图弹下的时候 不能销毁


    改变1.png
    改变2.png
    解决 滑动地图下来 不能化上去.png

    相关文章

      网友评论

          本文标题:手势-高仿抖音评论效果

          本文链接:https://www.haomeiwen.com/subject/npfzfctx.html