通过观察可以发现抖音的评论视图是从底部弹出的,包括顶部视图和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| 竖直移动。