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

使用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
    }
}

关键调整说明

  1. 时间戳排序:在touchesMoved中每次添加新点后,都按timestamp对数组排序,彻底解决点顺序错乱的问题。
  2. 图层复用:在初始化方法里就配置好pathLayer并添加到视图层,避免重复创建图层带来的性能损耗和绘制异常。
  3. 平滑曲线:用二次贝塞尔曲线替代直线连接点,让绘制的线条更自然流畅,即使偶尔有点的偏差,视觉上也不会太明显。
  4. Pencil专属处理:只响应Apple Pencil的触摸事件,避免手指触摸干扰绘图逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:59:39