使用Apple Pencil流畅绘图:部分点顺序错乱问题咨询
解决Apple Pencil绘图点顺序错乱的问题
嘿,我之前用Apple Pencil做绘图功能时也碰到过这个点顺序错乱的问题!主要是因为系统在传递Pencil触摸事件时,偶尔会因为调度延迟导致点的时序不是严格按你绘制的顺序来的。结合你的代码,我调整了一套解决方案,你可以参考下:
核心修改思路
- 用时间戳校准点顺序:Apple Pencil的
UITouch对象自带timestamp属性,我们可以用它来对收集到的点做排序,确保路径是严格按绘制时间构建的。 - 优化图层复用:不要每次触摸开始都新建
CAShapeLayer,初始化时就配置好并复用,提升性能。 - 平滑路径处理:加入贝塞尔曲线平滑算法,既让线条更流畅,也能缓解点顺序错乱带来的视觉卡顿。
修改后的完整代码
import Foundation import UIKit class CanvasView: UIView { // 存储带时间戳的触摸点,确保顺序可控 private var touchPoints: [TouchPoint] = [] // 复用的形状图层,避免重复创建 private let pathLayer = CAShapeLayer() override init(frame: CGRect) { super.init(frame: frame) setupDrawingLayer() } required init?(coder: NSCoder) { super.init(coder: coder) setupDrawingLayer() } // 初始化绘图图层的基础配置 private func setupDrawingLayer() { pathLayer.fillColor = UIColor.clear.cgColor pathLayer.strokeColor = UIColor.red.cgColor pathLayer.lineWidth = 3.0 pathLayer.lineCap = .round pathLayer.lineJoin = .round layer.addSublayer(pathLayer) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let pencilTouch = touches.first, pencilTouch.type == .pencil else { return } let point = pencilTouch.location(in: self) touchPoints.append(TouchPoint(location: point, timestamp: pencilTouch.timestamp)) updateDrawingPath() } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard let pencilTouch = touches.first, pencilTouch.type == .pencil else { return } let point = pencilTouch.location(in: self) touchPoints.append(TouchPoint(location: point, timestamp: pencilTouch.timestamp)) // 关键:按时间戳排序,强制校准点的顺序 touchPoints.sort { $0.timestamp < $1.timestamp } updateDrawingPath() } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { // 触摸结束后清空点数组,准备下一次绘制 touchPoints.removeAll() } // 更新绘制路径,加入平滑处理 private func updateDrawingPath() { guard touchPoints.count > 1 else { return } let path = UIBezierPath() path.move(to: touchPoints[0].location) // 使用二次贝塞尔曲线连接点,让线条更平滑 for index in 1..<touchPoints.count { let prevPoint = touchPoints[index-1].location let currentPoint = touchPoints[index].location let midPoint = CGPoint(x: (prevPoint.x + currentPoint.x)/2, y: (prevPoint.y + currentPoint.y)/2) if index == 1 { path.addLine(to: midPoint) } else { let lastMidPoint = CGPoint(x: (touchPoints[index-2].location.x + prevPoint.x)/2, y: (touchPoints[index-2].location.y + prevPoint.y)/2) path.addQuadCurve(to: midPoint, controlPoint: prevPoint) } } // 最后连接到最终的触摸点 path.addLine(to: touchPoints.last!.location) pathLayer.path = path.cgPath } // 自定义结构体,存储点的位置和对应的时间戳 private struct TouchPoint { let location: CGPoint let timestamp: TimeInterval } }
关键调整说明
- 时间戳排序:在
touchesMoved中每次添加新点后,都按timestamp对数组排序,彻底解决点顺序错乱的问题。 - 图层复用:在初始化方法里就配置好
pathLayer并添加到视图层,避免重复创建图层带来的性能损耗和绘制异常。 - 平滑曲线:用二次贝塞尔曲线替代直线连接点,让绘制的线条更自然流畅,即使偶尔有点的偏差,视觉上也不会太明显。
- Pencil专属处理:只响应Apple Pencil的触摸事件,避免手指触摸干扰绘图逻辑。
内容的提问来源于stack exchange,提问作者user2529173
相关产品推荐
相关产品推荐

