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

iOS绘图App(Swift)中阻止反向绘制的实现问题

解决方案:阻止SwiftyDraw反向绘制已完成路径

问题分析

SwiftyDraw内置的offset是累计的路径总移动距离,不会随反向滑动减小,因此无法直接用它判断反向绘制。我们需要通过跟踪绘制轨迹点并结合向量方向判断来拦截反向操作,同时适配辅助触控(AssistiveTouch)的精度问题。


实现步骤与代码示例

1. 核心变量定义

在你的绘图视图控制器中维护轨迹点数组和容错阈值:

import SwiftyDraw

class DrawingVC: UIViewController, SwiftyDrawDelegate {
    @IBOutlet weak var drawView: SwiftyDrawView!
    // 记录当前绘制的所有轨迹点
    private var drawnPoints: [CGPoint] = []
    // 容错阈值(避免微小抖动被误判为反向绘制)
    private let reverseThreshold: CGFloat = 6.0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        drawView.delegate = self
        drawView.assistiveTouchSwitch = true
    }
}

2. 代理方法中实现反向判断

在swiftyDraw(isDrawingIn:using:)中添加方向校验逻辑:

func swiftyDraw(isDrawingIn drawingView: SwiftyDrawView, using touch: UITouch) {
    let currentPoint = touch.location(in: drawingView)
    
    // 首次绘制或轨迹点不足时,直接记录点
    guard drawnPoints.count >= 2 else {
        drawnPoints.append(currentPoint)
        return
    }
    
    let lastPoint = drawnPoints.last!
    let secondLastPoint = drawnPoints[drawnPoints.count - 2]
    
    // 计算两段路径的向量:上一段轨迹的方向、当前移动的方向
    let pathVector = CGVector(dx: lastPoint.x - secondLastPoint.x, dy: lastPoint.y - secondLastPoint.y)
    let currentVector = CGVector(dx: currentPoint.x - lastPoint.x, dy: currentPoint.y - lastPoint.y)
    
    // 计算向量点积:点积为负表示方向完全相反
    let dotProduct = pathVector.dx * currentVector.dx + pathVector.dy * currentVector.dy
    
    // 过滤微小移动,适配AssistiveTouch的精度偏差
    let moveDistance = hypot(currentVector.dx, currentVector.dy)
    
    // 反向绘制时,忽略当前触摸事件
    if dotProduct < 0 && moveDistance > reverseThreshold {
        return
    }
    
    // 正常绘制,记录轨迹点
    drawnPoints.append(currentPoint)
}

3. 重置轨迹点数组

在绘制开始/结束时清空轨迹点,避免跨绘制任务的干扰:

func swiftyDraw(didStartDrawingIn drawingView: SwiftyDrawView) {
    drawnPoints.removeAll()
}

func swiftyDraw(didFinishDrawingIn drawingView: SwiftyDrawView) {
    drawnPoints.removeAll()
}

进阶优化:拦截回退到已绘制路径中间的情况

如果需要阻止用户滑回已绘制路径的任意位置(而非仅上一段的反向),可以添加线段距离校验:

// 辅助方法:判断当前点是否在已绘制路径的反向线段附近
private func isReverseToExistingPath(currentPoint: CGPoint, lastPoint: CGPoint) -> Bool {
    for i in 0..<drawnPoints.count - 1 {
        let p1 = drawnPoints[i]
        let p2 = drawnPoints[i+1]
        
        // 计算当前点到线段的垂直距离
        let distance = distanceFromPointToLine(point: currentPoint, lineP1: p1, lineP2: p2)
        if distance > reverseThreshold {
            continue
        }
        
        // 判断移动方向与线段方向是否相反
        let lineVector = CGVector(dx: p2.x - p1.x, dy: p2.y - p1.y)
        let currentVector = CGVector(dx: currentPoint.x - lastPoint.x, dy: currentPoint.y - lastPoint.y)
        let dotProduct = lineVector.dx * currentVector.dx + lineVector.dy * currentVector.dy
        
        if dotProduct < 0 {
            return true
        }
    }
    return false
}

// 计算点到线段的垂直距离
private func distanceFromPointToLine(point: CGPoint, lineP1: CGPoint, lineP2: CGPoint) -> CGFloat {
    let lineLength = hypot(lineP2.x - lineP1.x, lineP2.y - lineP1.y)
    guard lineLength > 0 else {
        return hypot(point.x - lineP1.x, point.y - lineP1.y)
    }
    let projectionRatio = ((point.x - lineP1.x) * (lineP2.x - lineP1.x) + (point.y - lineP1.y) * (lineP2.y - lineP1.y)) / (lineLength * lineLength)
    let clampedRatio = max(0, min(1, projectionRatio))
    let projectionPoint = CGPoint(x: lineP1.x + clampedRatio * (lineP2.x - lineP1.x), y: lineP1.y + clampedRatio * (lineP2.y - lineP1.y))
    return hypot(point.x - projectionPoint.x, point.y - projectionPoint.y)
}

修改代理方法调用这个进阶校验:

func swiftyDraw(isDrawingIn drawingView: SwiftyDrawView, using touch: UITouch) {
    let currentPoint = touch.location(in: drawingView)
    
    guard !drawnPoints.isEmpty else {
        drawnPoints.append(currentPoint)
        return
    }
    
    let lastPoint = drawnPoints.last!
    let moveDistance = hypot(currentPoint.x - lastPoint.x, currentPoint.y - lastPoint.y)
    
    if moveDistance < reverseThreshold {
        return
    }
    
    // 拦截任意反向到已绘制路径的操作
    if drawnPoints.count >= 2 && isReverseToExistingPath(currentPoint: currentPoint, lastPoint: lastPoint) {
        return
    }
    
    drawnPoints.append(currentPoint)
}

注意事项

  • reverseThreshold可根据屏幕尺寸和触摸精度调整(建议5-8pt);
  • 若需完全禁止回退绘制,可在反向判断时直接调用drawingView.reset()清空当前路径;
  • 辅助触控开启时,触摸坐标可能存在微小偏移,阈值设置需兼顾准确性和用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:02:36