SwiftUI中不使用PencilKit实现铅笔绘图效果的技术求助
实现SwiftUI铅笔绘图效果(无需PencilKit)
要模拟铅笔的自然纹理与笔触,我们可以从线条粗细随机变化、颗粒纹理叠加、颜色深浅波动三个维度入手,以下是基于你现有代码的针对性修改方案:
1. 扩展Line结构体,添加工具类型标记
首先修改Line.swift,新增工具类型枚举,让每条线记录自身的绘制工具,方便后续差异化渲染:
import SwiftUI enum DrawingTool { case pen, marker, pencil } struct Line { var points: [CGPoint] var color: Color var lineWidth: CGFloat var tool: DrawingTool // 新增:标记线条对应的绘制工具 }
2. 优化ContentView的工具选择逻辑
替换原来的三个独立Bool变量,改用统一的工具状态管理,简化逻辑:
替换工具状态变量
删除isPen、isMarker、isPencil,新增统一的选中工具状态:
@State private var selectedTool: DrawingTool = .pen
更新工具切换函数
修改三个工具按钮的触发逻辑,直接设置选中工具:
func penAction() { selectedTool = .pen } func pencilAction() { selectedTool = .pencil } func markerAction() { selectedTool = .marker }
更新按钮UI判断逻辑
基于selectedTool判断按钮高亮状态:
func pen() -> some View { Button(action: penAction) { Image(systemName: "paintbrush.pointed.fill") .font(.title) .foregroundColor(selectedTool == .pen ? selectedColor : .gray) } } func pencil() -> some View { Button(action: pencilAction) { Image(systemName: "pencil") .font(.title) .foregroundColor(selectedTool == .pencil ? selectedColor : .gray) } } func marker() -> some View { Button(action: markerAction) { Image(systemName: "paintbrush.fill") .font(.title) .foregroundColor(selectedTool == .marker ? selectedColor : .gray) } }
拖动创建线条时传入工具类型
修改DragGesture的onChanged逻辑,创建Line时传入当前选中的工具:
.gesture( DragGesture(minimumDistance: 0, coordinateSpace: .local) .onChanged({value in let position = value.location if value.translation == .zero { lines.append(Line( points: [position], color: selectedColor, lineWidth: selectedLineWidth, tool: selectedTool // 传入当前工具类型 )) } else { guard let lastIdx = lines.indices.last else { return } lines[lastIdx].points.append(position) } }) )
3. 实现铅笔的纹理绘制逻辑
在Canvas的渲染代码中,针对不同工具做差异化处理:
Canvas {ctx, size in for line in lines { var path = Path() path.addLines(line.points) switch line.tool { case .pen: // 钢笔:保持原有圆润笔触逻辑 ctx.stroke(path, with: .color(line.color), style: StrokeStyle(lineWidth: line.lineWidth, lineCap: .round, lineJoin: .round)) case .marker: // 马克笔:方头笔触 ctx.stroke(path, with: .color(line.color), style: StrokeStyle(lineWidth: line.lineWidth, lineCap: .square, lineJoin: .miter)) case .pencil: // 铅笔:模拟纹理+粗细随机变化 // 1. 生成铅笔颗粒纹理 let pencilTexture = Image(uiImage: generatePencilTexture(size: CGSize(width: 200, height: 200))) let texturePaint = ImagePaint(image: pencilTexture, scale: 0.1) // 2. 叠加多层随机偏移的细线条,模拟铅笔不均匀笔触 for offset in stride(from: -1.0, through: 1.0, by: 0.5) { let offsetPath = path.offsetBy(dx: CGFloat.random(in: -offset...offset), dy: CGFloat.random(in: -offset...offset)) let randomWidth = line.lineWidth * CGFloat.random(in: 0.7...1.2) let alpha = CGFloat.random(in: 0.6...0.9) // 颜色深浅波动 ctx.stroke(offsetPath, with: .color(line.color.opacity(alpha)), style: StrokeStyle(lineWidth: randomWidth, lineCap: .round, lineJoin: .round)) } // 3. 叠加纹理层,增强铅笔质感 ctx.stroke(path, with: texturePaint, style: StrokeStyle(lineWidth: line.lineWidth, lineCap: .round, lineJoin: .round)) } } }
新增铅笔纹理生成函数
在ContentView中添加辅助函数,生成模拟铅笔颗粒的噪声纹理:
// 生成铅笔纹理(基于CoreGraphics随机噪点) private func generatePencilTexture(size: CGSize) -> UIImage { UIGraphicsBeginImageContextWithOptions(size, false, 0) guard let context = UIGraphicsGetCurrentContext() else { return UIImage() } // 填充白色底色 context.setFillColor(UIColor.white.cgColor) context.fill(CGRect(origin: .zero, size: size)) // 绘制灰色噪点,模拟铅笔石墨质感 for _ in 0..<Int(size.width * size.height * 0.05) { let x = CGFloat.random(in: 0...size.width) let y = CGFloat.random(in: 0...size.height) let gray = CGFloat.random(in: 0.2...0.6) context.setFillColor(UIColor(red: gray, green: gray, blue: gray, alpha: 0.3).cgColor) context.fillEllipse(in: CGRect(x: x, y: y, width: 1, height: 1)) } let image = UIGraphicsGetImageFromCurrentImageContext() ?? UIImage() UIGraphicsEndImageContext() return image }
4. 清理冗余代码
删除原来的isPen、isMarker、isPencil三个Bool变量,以及lineCapIs()函数,所有工具逻辑已整合到selectedTool的switch判断中。
效果说明
- 铅笔线条具备随机粗细波动和颜色深浅变化,叠加的噪点纹理会模拟出铅笔在纸上的颗粒感
- 保留了钢笔的圆润笔触和马克笔的方头笔触,工具切换逻辑更简洁清晰
内容的提问来源于stack exchange,提问作者Jay
相关产品推荐
相关产品推荐

