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
相关产品推荐
相关产品推荐

