You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UIBezierPath contains:方法无法识别线宽大于1像素的触摸

解决UIBezierPath线宽触摸检测问题

问题原因

UIBezierPath的contains(_:)方法仅判断点是否位于路径的填充区域内,而非描边区域。你当前创建的路径只是线条的中心路径,即便设置了lineWidth,路径本身的填充范围仍为这条1像素的中心线,因此只有精准点击到中心线才会触发检测。

解决方案

方法1:生成包含描边区域的路径

利用CGPath的copy(strokingWithWidth:lineCap:lineJoin:miterLimit:transform:)方法,生成一个代表描边区域的新路径,用该路径进行触摸检测:

private func hitTest(tapLocation: CGPoint){
    let originalPath: UIBezierPath = path0
    // 生成覆盖描边范围的路径
    let strokedPath = UIBezierPath(
        cgPath: originalPath.cgPath.copy(
            strokingWithWidth: originalPath.lineWidth,
            lineCap: originalPath.lineCapStyle,
            lineJoin: originalPath.lineJoinStyle,
            miterLimit: originalPath.miterLimit
        )
    )
    
    if strokedPath.contains(tapLocation){
       print("inside")
    } else{
        print("outside")
    }
}

方法2:直接使用CGPath的描边检测

调用CGPath的contains(_:using:transform:strokingWithWidth:lineCap:lineJoin:miterLimit:)方法,直接指定以描边模式判断点是否在范围内:

private func hitTest(tapLocation: CGPoint){
    let cgPath = path0.cgPath
    let isInside = cgPath.contains(
        tapLocation,
        using: .evenOdd,
        transform: .identity,
        strokingWithWidth: path0.lineWidth,
        lineCap: path0.lineCapStyle,
        lineJoin: path0.lineJoinStyle,
        miterLimit: path0.miterLimit
    )
    
    if isInside {
        print("inside")
    } else {
        print("outside")
    }
}

额外优化建议

  • 移除原路径代码中的path.close():直线路径无需闭合,闭合操作会额外添加从终点回到起点的线段,可能干扰检测。
  • 移除path.stroke():你使用CAShapeLayer负责绘制,这行代码仅在draw(_:)方法中有效,当前场景下属于冗余操作。

内容的提问来源于stack exchange,提问作者Dvole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:04:59