封装了一个仿照抖音效果的iOS评论弹窗
需求背景
开发一个类似抖音评论弹窗交互效果的弹窗,支持滑动消失,
滑动查看评论
效果如下图
思路
创建一个视图,该视图上面放置一个tableView, 该视图上添加一个滑动手势,同时设置代理,实现代理方法
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
支持同时响应手势,就是为了我们tableView滚动到顶部的时候,继续滚动父亲视图,达到连续滑动的效果,如果不是设置同时响应的话,我们滚动到tableView顶部,继续向下滑动的话,整个弹窗是不会向下滑动的,同时,滚动到顶部的时候,要设置tableView.pangesture.enabled = NO,否则反复来回滑动的时候,会造成两个视图同时滚动的效果
代码
// // LBCommentPopView.m // TEXT // // Created by mac on 2024/7/7. // Copyright © 2024 刘博. All rights reserved. // #import "LBCommentPopView.h" #import "LBFunctionTestHeader.h" @interface LBCommentPopView () @property (nonatomic, strong) UITapGestureRecognizer *tapGesture; @property (nonatomic, strong) UIPanGestureRecognizer *panGesture; @property (nonatomic, weak) UIScrollView *scrollView; @property (nonatomic, assign) BOOL isDragScrollView; @property (nonatomic, assign) CGFloat lastTransitionY; @end @implementation LBCommentPopView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self createRecognizer]; } return self; } - (void)createRecognizer { [self addGestureRecognizer:self.tapGesture]; [self addGestureRecognizer:self.panGesture]; } - (void)show:(void (^)(void))completion { self.hidden = NO; [UIView animateWithDuration:0.25f animations:^{ CGRect frame = self.containerView.frame; frame.origin.y = self.frame.size.height - frame.size.height; self.containerView.frame = frame; } completion:^(BOOL finished) { !completion ? : completion(); }]; } - (void)dismiss { [UIView animateWithDuration:0.25f animations:^{ CGRect frame = self.containerView.frame; frame.origin.y = ScreenHeight; self.containerView.frame = frame; }completion:^(BOOL finished) { self.hidden = YES; }]; } #pragma mark - UIGestureRecognizerDelegate - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { if (gestureRecognizer == self.panGesture) { UIView *touchView = touch.view; while (touchView != nil) { if ([touchView isKindOfClass:[UIScrollView class]]) { self.scrollView = (UIScrollView *)touchView; self.isDragScrollView = YES; break; }else if (touchView == self.containerView) { self.isDragScrollView = NO; break; } touchView = (UIView *)[touchView nextResponder]; } } return YES; } - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer == self.tapGesture) { CGPoint point = [gestureRecognizer locationInView:self.containerView]; if ([self.containerView.layer containsPoint:point] && gestureRecognizer.view == self) { return NO; } }else if (gestureRecognizer == self.panGesture) { return YES; } return YES; } // 是否与其他手势共存 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if (gestureRecognizer == self.panGesture) { if ([otherGestureRecognizer isKindOfClass:NSClassFromString(@"UIScrollViewPanGestureRecognizer")] || [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]]) { return YES; } } } return NO; } #pragma mark - HandleGesture - (void)handleTapGesture:(UITapGestureRecognizer *)tapGesture { CGPoint point = [tapGesture locationInView:self.containerView]; if (![self.containerView.layer containsPoint:point] && tapGesture.view == self) { [self dismiss]; } } - (void)handlePanGesture:(UIPanGestureRecognizer *)panGesture { CGPoint translation = [panGesture translationInView:self.containerView]; if (self.isDragScrollView) { // 当UIScrollView在最顶部时,处理视图的滑动 if (self.scrollView.contentOffset.y 0) { // 向下拖拽 self.scrollView.contentOffset = CGPointZero; self.scrollView.panGestureRecognizer.enabled = NO; self.isDragScrollView = NO; CGRect contentFrame = self.containerView.frame; contentFrame.origin.y += translation.y; self.containerView.frame = contentFrame; } } }else { CGFloat contentM = (self.frame.size.height - self.containerView.frame.size.height); if (translation.y > 0) { // 向下拖拽 CGRect contentFrame = self.containerView.frame; contentFrame.origin.y += translation.y; self.containerView.frame = contentFrame; }else if (translation.y contentM) { // 向上拖拽 CGRect contentFrame = self.containerView.frame; contentFrame.origin.y = MAX((self.containerView.frame.origin.y + translation.y), contentM); self.containerView.frame = contentFrame; } } [panGesture setTranslation:CGPointZero inView:self.containerView]; if (panGesture.state == UIGestureRecognizerStateEnded) { CGPoint velocity = [panGesture velocityInView:self.containerView]; self.scrollView.panGestureRecognizer.enabled = YES; // 结束时的速度>0 滑动距离> 5 且UIScrollView滑动到最顶部 NSLog(@"%f", self.lastTransitionY); if (velocity.y > 0 && self.lastTransitionY > 5 && !self.isDragScrollView) { [self dismiss]; }else { [self show:^{ }]; } } self.lastTransitionY = translation.y; } #pragma mark - lazy load - (UITapGestureRecognizer *)tapGesture { if (!_tapGesture) { _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; _tapGesture.delegate = self; } return _tapGesture; } - (UIPanGestureRecognizer *)panGesture { if (!_panGesture) { _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)]; _panGesture.delegate = self; } return _panGesture; } @end
demo link
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。