手势-高仿抖音评论效果

2019-06-17  本文已影响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太过灵巧

#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
上一篇下一篇

猜你喜欢

热点阅读