使用UIBezierPath绘制多条路径时首条消失且报错求助
嘿,我来帮你拆解下问题根源,然后给出可行的修复方案~
问题分析
你的代码里有两个核心问题:
- 第一条路径消失:每次在
.changed状态时,你直接把lineShape.path替换成了当前新画的单条路径,完全覆盖了之前的路径,所以旧线条自然就看不到了。而且你复用了同一个CAShapeLayer,却没有把新路径和旧路径合并。 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
相关产品推荐
相关产品推荐

