手势-高仿抖音评论效果
2019-06-17 本文已影响0人
守护地中海的花
要求:1.不拖拽视图 自然弹上弹下 2.拖拽视图滑动到一定位置选择回弹或销毁
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