UIKit绘图时快速画线UITouch的touchMoved无法连续回调问题
解决UIKit快速画线时UITouch回调不连续的问题
这种情况太常见了——iOS系统为了性能优化,在快速滑动触摸的时候,会自动合并一些相邻的触摸点,导致touchesMoved不会每一个像素都触发回调,这就是你觉得线条断断续续的根源。下面给你两个实用的解决方案:
方案一:插值补全缺失的触摸点
核心思路是:每次收到touchesMoved回调时,计算当前触摸点和上一个点之间的所有中间插值点,逐个绘制图案,填补系统跳过的点,保证线条连续。
补全后的触摸事件处理代码如下:
var tempImageView: UIImageView! var patternImage = UIImage(named: "xxx.png")! var brushWidth: CGFloat = 50.0 var opacity: CGFloat = 1.0 var lastPoint = CGPoint.zero override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) guard let touch = touches.first else { return } lastPoint = touch.location(in: tempImageView) } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) guard let touch = touches.first else { return } let currentPoint = touch.location(in: tempImageView) // 计算插值点,补全中间缺失的路径 drawInterpolatedPoints(from: lastPoint, to: currentPoint) lastPoint = currentPoint } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) // 收尾绘制,避免线条末端不完整 guard let touch = touches.first else { return } let currentPoint = touch.location(in: tempImageView) drawInterpolatedPoints(from: lastPoint, to: currentPoint) } // 插值绘制的核心方法 private func drawInterpolatedPoints(from start: CGPoint, to end: CGPoint) { let distance = hypot(end.x - start.x, end.y - start.y) let steps = Int(distance / 2) // 每2个像素画一个点,可根据需求调整密度 UIGraphicsBeginImageContextWithOptions(tempImageView.bounds.size, false, UIScreen.main.scale) tempImageView.image?.draw(in: tempImageView.bounds) for step in 0...steps { let t = CGFloat(step) / CGFloat(steps) let interpolatedX = start.x + t * (end.x - start.x) let interpolatedY = start.y + t * (end.y - start.y) let point = CGPoint(x: interpolatedX, y: interpolatedY) // 把图案绘制到当前插值点(居中对齐) patternImage.draw(at: CGPoint(x: point.x - brushWidth/2, y: point.y - brushWidth/2), blendMode: .normal, alpha: opacity) } tempImageView.image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() }
方案二:用图案描边路径(更高效)
如果你的需求是绘制连续的图案线条,直接用UIBezierPath创建路径,再将图案设置为路径的描边样式会更合适——系统会自动处理路径连续性,即使touchesMoved回调不频繁,线条依然完整,而且绘制效率更高。
示例代码:
var tempImageView: UIImageView! var patternImage = UIImage(named: "xxx.png")! var brushWidth: CGFloat = 50.0 var opacity: CGFloat = 1.0 var currentPath = UIBezierPath() override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) guard let touch = touches.first else { return } let point = touch.location(in: tempImageView) currentPath.move(to: point) } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) guard let touch = touches.first else { return } let point = touch.location(in: tempImageView) currentPath.addLine(to: point) updateDrawing() } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) updateDrawing() // 重置路径,准备下一次绘制 currentPath = UIBezierPath() } private func updateDrawing() { UIGraphicsBeginImageContextWithOptions(tempImageView.bounds.size, false, UIScreen.main.scale) tempImageView.image?.draw(in: tempImageView.bounds) // 创建基于图案的描边颜色 let patternColor = UIColor(patternImage: patternImage) patternColor.setStroke() currentPath.lineWidth = brushWidth currentPath.lineCapStyle = .round // 圆角端点让线条更顺滑 currentPath.stroke(with: .normal, alpha: opacity) tempImageView.image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() }
额外提醒:如果你的图案带透明通道,一定要确保UIGraphicsBeginImageContextWithOptions的第二个参数传false,这样才能保留透明效果。
内容的提问来源于stack exchange,提问作者iOS_Developer
相关产品推荐
相关产品推荐

