iOS中如何让开启userInteractionEnabled的Overlay视图传递触摸事件给下层UIView
解决Overlay视图与UIScrollView同时响应触摸事件的方案
这个问题我之前做iOS项目时也碰到过,本质是iOS触摸事件的“独占性”导致的——默认情况下一个触摸事件只会被一个响应者处理。要让两者都正常工作,核心是打破这种独占,给你两个落地可行的方案:
方案1:自定义Overlay视图,通过重写触摸判断方法分流事件
如果你的Overlay视图只有特定区域需要响应触摸(比如某个按钮区域),或者你想让Overlay先判断是否要处理事件,再决定是否把事件传给ScrollView,这个方案最适合:
- 创建一个继承自
UIImageView的子类(比如TouchPassThroughImageView) - 重写
pointInside:withEvent:方法,根据需求决定是否拦截事件:- 如果触摸点在Overlay需要响应的范围内,返回
YES(自己处理事件) - 如果不在这个范围内,返回
NO,让事件继续向下传递给底层的UIScrollView
- 如果触摸点在Overlay需要响应的范围内,返回
代码示例:
@interface TouchPassThroughImageView : UIImageView @end @implementation TouchPassThroughImageView - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { // 假设你只需要Overlay的中心100x100区域响应触摸 CGRect targetRect = CGRectMake(self.bounds.size.width/2 - 50, self.bounds.size.height/2 - 50, 100, 100); if (CGRectContainsPoint(targetRect, point)) { return YES; // 自己处理这个触摸 } return NO; // 不拦截,让事件传给ScrollView } @end
如果你的Overlay整个区域都需要响应触摸,同时还要让ScrollView能滑动,可以在重写hitTest:withEvent:时,先获取自己的响应者,再手动把事件传递给ScrollView:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = [super hitTest:point withEvent:event]; // 如果当前视图是触摸目标,同时把事件传递给ScrollView if (hitView == self) { // 找到底层的ScrollView(这里假设ScrollView是self.superview的子视图,根据你的层级调整) UIScrollView *scrollView = (UIScrollView *)[self.superview viewWithTag:100]; // 假设ScrollView的tag是100 [scrollView touchesBegan:event.allTouches withEvent:event]; } return hitView; }
方案2:利用手势识别器实现共存
如果你的Overlay只需要处理点击事件,ScrollView需要处理滑动,用手势识别器的共存机制更简单:
- 给Overlay添加
UITapGestureRecognizer,并设置cancelsTouchesInView = NO(这样不会阻止底层视图接收触摸事件) - 设置手势识别器的代理,实现
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:方法,返回YES,允许ScrollView的滑动手势和Overlay的点击手势同时识别
代码示例:
// 初始化Overlay和手势 UIImageView *overlayView = [[UIImageView alloc] initWithImage:overlayImage]; overlayView.userInteractionEnabled = YES; overlayView.frame = self.view.bounds; UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(overlayTapped:)]; tapGesture.cancelsTouchesInView = NO; tapGesture.delegate = self; [overlayView addGestureRecognizer:tapGesture]; // 添加到视图层级 [self.view addSubview:self.scrollView]; [self.view addSubview:overlayView]; // 实现代理方法 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { // 判断另一个手势是否是ScrollView的滑动手势 if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]]) { return YES; } return NO; } // Overlay点击事件处理 - (void)overlayTapped:(UITapGestureRecognizer *)gesture { // 你的点击逻辑 }
注意事项
- 不管用哪种方案,都要确保视图层级正确:ScrollView在底层,Overlay在顶层
- 如果用方案1的自定义视图,记得把你的Overlay视图替换成这个子类
- 如果ScrollView有复杂的手势交互,建议优先用方案2,因为手势识别器的共存机制是苹果官方推荐的方式
内容的提问来源于stack exchange,提问作者Laurent Crivello
相关产品推荐
相关产品推荐

