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

使用UIBezierPath绘制多条路径时首条消失且报错求助

嘿,我来帮你拆解下问题根源,然后给出可行的修复方案~

问题分析

你的代码里有两个核心问题:

  1. 第一条路径消失:每次在.changed状态时,你直接把lineShape.path替换成了当前新画的单条路径,完全覆盖了之前的路径,所以旧线条自然就看不到了。而且你复用了同一个CAShapeLayer,却没有把新路径和旧路径合并。
  2. CGPathCloseSubpath: no current point报错:在.began状态你把一个空的UIBezierPath塞进了数组,到.ended时取这个空路径调用close()——但这个路径连一个点都没有,系统当然会报错。另外,你画的是直线,其实根本不需要调用close()(这个方法是用来闭合多边形/曲线的)。

修复后的代码

我调整了实现逻辑,用两个图层分别管理已完成的路径和正在绘制的临时路径,同时维护一个主路径来累计所有线条:

private lazy var finishedShapeLayer: CAShapeLayer = {
    let layer = CAShapeLayer()
    layer.strokeColor = UIColor.blue.cgColor
    layer.lineWidth = 2.0
    layer.fillColor = UIColor.clear.cgColor // 只显示线条,不填充
    return layer
}()

private lazy var tempShapeLayer: CAShapeLayer = {
    let layer = CAShapeLayer()
    layer.strokeColor = UIColor.blue.cgColor
    layer.lineWidth = 2.0
    layer.fillColor = UIColor.clear.cgColor
    return layer
}()

// 主路径:保存所有已完成的线条
private var mainBezierPath = UIBezierPath()
private var panStartPoint: CGPoint = .zero

private lazy var panRecognizer: UIPanGestureRecognizer = {
    let recognizer = UIPanGestureRecognizer(target: self, action: #selector(panGestureHandler(_:)))
    return recognizer
}()

override func viewDidLoad() {
    super.viewDidLoad()
    // 提前添加图层,避免重复添加
    view.layer.addSublayer(finishedShapeLayer)
    view.layer.addSublayer(tempShapeLayer)
    view.addGestureRecognizer(panRecognizer)
}

@objc func panGestureHandler(_ recognizer: UIPanGestureRecognizer) {
    let currentPoint = recognizer.location(in: view)
    
    switch recognizer.state {
    case .began:
        panStartPoint = currentPoint
        // 初始化临时路径的起点
        let tempPath = UIBezierPath()
        tempPath.move(to: panStartPoint)
        tempShapeLayer.path = tempPath.cgPath
        
    case .changed:
        // 实时更新临时路径,显示当前拖拽的线条
        let tempPath = UIBezierPath()
        tempPath.move(to: panStartPoint)
        tempPath.addLine(to: currentPoint)
        tempShapeLayer.path = tempPath.cgPath
        
    case .ended, .cancelled:
        // 把当前完成的线条合并到主路径
        let finishedPath = UIBezierPath()
        finishedPath.move(to: panStartPoint)
        finishedPath.addLine(to: currentPoint)
        mainBezierPath.append(finishedPath)
        
        // 更新已完成图层的路径,保留所有历史线条
        finishedShapeLayer.path = mainBezierPath.cgPath
        // 清除临时图层,准备下一次绘制
        tempShapeLayer.path = nil
        
    default:
        tempShapeLayer.path = nil
    }
}

关键修改点说明

  • 拆分图层:用finishedShapeLayer存所有已完成的线条,tempShapeLayer只显示当前正在画的临时线条,拖拽时不会干扰已完成的内容,交互更流畅。
  • 主路径累计:用mainBezierPath来叠加所有完成的线条,确保每次新画的线条都会和旧线条一起显示。
  • 修复报错:不再往数组里塞空路径,而是在.ended时生成完整的线条路径再合并,同时去掉了不必要的close()调用。
  • 提前初始化图层:在viewDidLoad里就把图层添加到视图,避免重复添加图层导致的冗余问题。

额外小建议

如果需要支持撤销功能,可以把每次完成的路径存在一个数组里,撤销时移除最后一条,再重新生成主路径;也可以给临时线条设置不同的颜色/透明度,区分正在绘制和已完成的内容,提升体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 07:42:50