iOS带曲线视图的点击事件冲突问题:曲线区域点击识别
解决CurveView遮罩区域点击穿透问题
这个问题的核心原因很明确:CAShapeLayer的mask仅负责视觉上裁剪视图,不会改变视图的点击响应边界。你的CurveView虽然通过mask实现了底部曲线的视觉效果,但它的可点击范围还是完整的矩形frame,所以黄色视图被绿色视图覆盖的区域(红色高亮区),点击时依然会触发绿色视图的tap手势。
要解决这个问题,最精准的方式是重写CurveView的hitTest(_:with:)方法,让视图只响应mask路径范围内的点击,超出路径的点击则穿透到下方视图。
修改CurveView类,添加点击区域判断
class CurveView: UIView { // 保存mask的路径,避免每次hitTest都重新创建 private var maskPath: CGPath? required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override func layoutSubviews() { super.layoutSubviews() self.createBottomCurve() } private func createBottomCurve() { let offset: CGFloat = self.frame.width / 1.2 let bounds: CGRect = self.bounds let rectBounds: CGRect = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.size.width, height: bounds.size.height / 2) let rectPath: UIBezierPath = UIBezierPath(rect: rectBounds) let ovalBounds: CGRect = CGRect(x: bounds.origin.x - offset / 2, y: bounds.origin.y, width: bounds.size.width + offset, height: bounds.size.height) let ovalPath: UIBezierPath = UIBezierPath(ovalIn: ovalBounds) rectPath.append(ovalPath) let maskLayer: CAShapeLayer = CAShapeLayer() maskLayer.frame = bounds maskLayer.path = rectPath.cgPath self.layer.mask = maskLayer // 保存mask的路径,供hitTest使用 maskPath = rectPath.cgPath } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { guard let path = maskPath else { return super.hitTest(point, with: event) } // 判断点击点是否在mask路径范围内 if path.contains(point) { return super.hitTest(point, with: event) } else { // 不在范围内,返回nil,点击事件会穿透到下方视图 return nil } } }
代码说明
- 新增
maskPath属性保存遮罩路径,避免每次hitTest重复生成路径,提升性能; - 重写
hitTest(_:with:)方法:- 先校验点击点是否在
maskPath的范围内; - 若在范围内则正常响应点击,若不在则返回
nil,系统会自动向下查找可响应的视图(也就是你的黄色视图)。
- 先校验点击点是否在
其他可选方案
如果CurveView的曲线区域完全不需要交互,也可以通过调整视图层级或直接裁剪视图的方式解决,但上面的hitTest方案是最灵活且精准的——它完全匹配你视觉上的可交互区域。
内容的提问来源于stack exchange,提问作者Francis F
相关产品推荐
相关产品推荐

