CGContext strokePath绘制笔触边缘模糊,如何实现清晰手绘注释?
手绘应用笔触模糊问题解决方案
问题描述
我正在开发一款简单的手绘应用,使用UIGesture类跟踪笔触(即铅笔或手指的移动),随后在UIImageView上绘制这些触摸点。相关示例代码如下:
let previousLocation = touch.previousLocation(in: self) let location = touch.location(in: self) var lineWidth: CGFloat = 3 UIColor.black.setStroke() context?.setLineWidth(lineWidth) context?.setLineCap(.round) context?.move(to: previousLocation) context?.addLine(to: location) context?.strokePath()
绘制出的笔触边缘模糊,效果如图:
已尝试以下方法但未彻底解决:
- 使用贝塞尔路径绘制
- 用View替代ImageView
- 增大UIGraphicsBeginImageContextWithOptions的缩放比例(虽提升清晰度但导致书写卡顿)
请问如何实现清晰的手绘注释?
解决方案
1. 适配Retina屏幕的绘图上下文
确保绘图上下文的缩放因子与屏幕一致,同时上下文尺寸严格匹配控件显示尺寸,避免过度放大导致卡顿:
// 获取屏幕原生缩放比例 let scale = UIScreen.main.scale // 创建与ImageView尺寸匹配的上下文,启用Retina支持 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, false, scale) // 执行你的绘图逻辑... // 结束上下文并更新图片 imageView.image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext()
2. 平滑触摸路径提升清晰度
单纯连接相邻触摸点易出现折线和边缘模糊,通过收集多点生成平滑贝塞尔曲线优化:
// 存储触摸点的数组,用于路径平滑 private var touchPoints = [CGPoint]() override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let currentPoint = touch.location(in: self) touchPoints.append(currentPoint) // 点数量足够时生成平滑路径 if touchPoints.count > 3 { let path = UIBezierPath() path.move(to: touchPoints[0]) // 用二次贝塞尔曲线连接中间点 for i in 1..<touchPoints.count-2 { let midPoint = CGPoint(x: (touchPoints[i].x + touchPoints[i+1].x)/2, y: (touchPoints[i].y + touchPoints[i+1].y)/2) path.addQuadCurve(to: midPoint, controlPoint: touchPoints[i]) } // 连接最后一个点 path.addLine(to: touchPoints.last!) // 设置路径属性 UIColor.black.setStroke() path.lineWidth = 3 path.lineCapStyle = .round path.lineJoinStyle = .round path.stroke() // 更新ImageView imageView.image = UIGraphicsGetImageFromCurrentImageContext() // 保留最后3个点用于后续平滑 touchPoints = Array(touchPoints.suffix(3)) } }
3. 使用CAShapeLayer实现GPU加速绘制
改用CAShapeLayer替代ImageView缓存绘制,利用GPU实时渲染,既保证清晰度又避免卡顿:
private let drawLayer = CAShapeLayer() override func viewDidLoad() { super.viewDidLoad() // 配置ShapeLayer属性 drawLayer.strokeColor = UIColor.black.cgColor drawLayer.lineWidth = 3 drawLayer.lineCap = .round drawLayer.lineJoin = .round drawLayer.fillColor = UIColor.clear.cgColor // 添加到绘制视图的图层中 drawView.layer.addSublayer(drawLayer) } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let startPoint = touch.location(in: drawView) // 初始化路径 let path = UIBezierPath() path.move(to: startPoint) drawLayer.path = path.cgPath } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { guard let touch = touches.first else { return } let currentPoint = touch.location(in: drawView) // 扩展路径 let mutablePath = CGMutablePath(path: drawLayer.path!) mutablePath.addLine(to: currentPoint) drawLayer.path = mutablePath }
4. 禁用ImageView的自动缩放
确保ImageView的内容模式不会拉伸图片,避免模糊:
imageView.contentMode = .topLeft imageView.clipsToBounds = true
内容的提问来源于stack exchange,提问作者Raj Kiran
相关产品推荐
相关产品推荐

