如何在Swift中创建铅笔手绘风格的视图边框?
嘿,要实现铅笔手绘风格的视图边框,我有几个实用的Swift方案分享给你,亲测能做出那种随性的手绘感~
方法一:用CAShapeLayer生成随机抖动边框
这种方式通过创建带随机偏移的路径,模拟手绘时的手部抖动,再用CAShapeLayer把路径作为边框添加到视图上,灵活度很高。
实现代码
import UIKit extension UIView { func addPencilBorder(lineWidth: CGFloat = 2.0, color: UIColor = .black) { let shapeLayer = CAShapeLayer() shapeLayer.lineWidth = lineWidth shapeLayer.strokeColor = color.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.lineCap = .round // 模拟铅笔笔触的圆润感 shapeLayer.lineJoin = .round // 生成带抖动效果的手绘路径 let path = UIBezierPath() let drawBounds = self.bounds.insetBy(dx: lineWidth/2, dy: lineWidth/2) let cornerRadius = self.layer.cornerRadius // 上边:从左到右添加随机偏移的点 var prevPoint = CGPoint(x: drawBounds.minX, y: drawBounds.minY + cornerRadius) path.move(to: prevPoint) for x in stride(from: drawBounds.minX, to: drawBounds.maxX, by: 2.0) { let randomY = drawBounds.minY + cornerRadius + CGFloat.random(in: -1.0...1.0) path.addLine(to: CGPoint(x: x, y: randomY)) prevPoint = CGPoint(x: x, y: randomY) } path.addLine(to: CGPoint(x: drawBounds.maxX, y: drawBounds.minY + cornerRadius)) // 右上角圆角 path.addArc(withCenter: CGPoint(x: drawBounds.maxX - cornerRadius, y: drawBounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi * 1.5, endAngle: 0, clockwise: true) // 右边:从上到下添加随机偏移的点 prevPoint = CGPoint(x: drawBounds.maxX - cornerRadius, y: drawBounds.minY) path.move(to: prevPoint) for y in stride(from: drawBounds.minY, to: drawBounds.maxY, by: 2.0) { let randomX = drawBounds.maxX - cornerRadius + CGFloat.random(in: -1.0...1.0) path.addLine(to: CGPoint(x: randomX, y: y)) prevPoint = CGPoint(x: randomX, y: y) } path.addLine(to: CGPoint(x: drawBounds.maxX - cornerRadius, y: drawBounds.maxY)) // 右下角圆角 path.addArc(withCenter: CGPoint(x: drawBounds.maxX - cornerRadius, y: drawBounds.maxY - cornerRadius), radius: cornerRadius, startAngle: 0, endAngle: .pi * 0.5, clockwise: true) // 下边:从右到左添加随机偏移的点 prevPoint = CGPoint(x: drawBounds.maxX, y: drawBounds.maxY - cornerRadius) path.move(to: prevPoint) for x in stride(from: drawBounds.maxX, to: drawBounds.minX, by: -2.0) { let randomY = drawBounds.maxY - cornerRadius + CGFloat.random(in: -1.0...1.0) path.addLine(to: CGPoint(x: x, y: randomY)) prevPoint = CGPoint(x: x, y: randomY) } path.addLine(to: CGPoint(x: drawBounds.minX, y: drawBounds.maxY - cornerRadius)) // 左下角圆角 path.addArc(withCenter: CGPoint(x: drawBounds.minX + cornerRadius, y: drawBounds.maxY - cornerRadius), radius: cornerRadius, startAngle: .pi * 0.5, endAngle: .pi, clockwise: true) // 左边:从下到上添加随机偏移的点 prevPoint = CGPoint(x: drawBounds.minX + cornerRadius, y: drawBounds.maxY) path.move(to: prevPoint) for y in stride(from: drawBounds.maxY, to: drawBounds.minY, by: -2.0) { let randomX = drawBounds.minX + cornerRadius + CGFloat.random(in: -1.0...1.0) path.addLine(to: CGPoint(x: randomX, y: y)) prevPoint = CGPoint(x: randomX, y: y) } // 左上角圆角闭合路径 path.addArc(withCenter: CGPoint(x: drawBounds.minX + cornerRadius, y: drawBounds.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: .pi * 1.5, clockwise: true) shapeLayer.path = path.cgPath self.layer.addSublayer(shapeLayer) } }
使用方式
直接给目标视图调用方法即可:
// 假设你有一个名为myView的视图 myView.layer.cornerRadius = 12 // 可选:如果需要圆角边框 myView.addPencilBorder(lineWidth: 2.5, color: .darkGray)
方法二:自定义UIView重写draw方法
如果你的视图需要动态调整(比如随布局变化),可以自定义一个UIView子类,在draw(_:)方法里直接绘制手绘边框,适配性更好。
实现代码
class PencilBorderView: UIView { // 可配置的边框属性 var lineWidth: CGFloat = 2.0 { didSet { setNeedsDisplay() } } var borderColor: UIColor = .black { didSet { setNeedsDisplay() } } override func draw(_ rect: CGRect) { super.draw(rect) guard let context = UIGraphicsGetCurrentContext() else { return } context.setStrokeColor(borderColor.cgColor) context.setLineWidth(lineWidth) context.setLineCap(.round) context.setLineJoin(.round) let drawRect = rect.insetBy(dx: lineWidth/2, dy: lineWidth/2) let cornerRadius = self.layer.cornerRadius // 绘制上边 var startPoint = CGPoint(x: drawRect.minX, y: drawRect.minY + cornerRadius) context.move(to: startPoint) for x in stride(from: drawRect.minX, to: drawRect.maxX, by: 1.5) { let randomY = drawRect.minY + cornerRadius + CGFloat.random(in: -0.8...0.8) context.addLine(to: CGPoint(x: x, y: randomY)) } context.addLine(to: CGPoint(x: drawRect.maxX, y: drawRect.minY + cornerRadius)) // 右上角圆角 context.addArc(center: CGPoint(x: drawRect.maxX - cornerRadius, y: drawRect.minY + cornerRadius), radius: cornerRadius, startAngle: .pi * 1.5, endAngle: 0, clockwise: true) // 绘制右边 startPoint = CGPoint(x: drawRect.maxX - cornerRadius, y: drawRect.minY) context.move(to: startPoint) for y in stride(from: drawRect.minY, to: drawRect.maxY, by: 1.5) { let randomX = drawRect.maxX - cornerRadius + CGFloat.random(in: -0.8...0.8) context.addLine(to: CGPoint(x: randomX, y: y)) } context.addLine(to: CGPoint(x: drawRect.maxX - cornerRadius, y: drawRect.maxY)) // 右下角圆角 context.addArc(center: CGPoint(x: drawRect.maxX - cornerRadius, y: drawRect.maxY - cornerRadius), radius: cornerRadius, startAngle: 0, endAngle: .pi * 0.5, clockwise: true) // 绘制下边 startPoint = CGPoint(x: drawRect.maxX, y: drawRect.maxY - cornerRadius) context.move(to: startPoint) for x in stride(from: drawRect.maxX, to: drawRect.minX, by: -1.5) { let randomY = drawRect.maxY - cornerRadius + CGFloat.random(in: -0.8...0.8) context.addLine(to: CGPoint(x: x, y: randomY)) } context.addLine(to: CGPoint(x: drawRect.minX, y: drawRect.maxY - cornerRadius)) // 左下角圆角 context.addArc(center: CGPoint(x: drawRect.minX + cornerRadius, y: drawRect.maxY - cornerRadius), radius: cornerRadius, startAngle: .pi * 0.5, endAngle: .pi, clockwise: true) // 绘制左边 startPoint = CGPoint(x: drawRect.minX + cornerRadius, y: drawRect.maxY) context.move(to: startPoint) for y in stride(from: drawRect.maxY, to: drawRect.minY, by: -1.5) { let randomX = drawRect.minX + cornerRadius + CGFloat.random(in: -0.8...0.8) context.addLine(to: CGPoint(x: randomX, y: y)) } // 左上角圆角闭合路径 context.addArc(center: CGPoint(x: drawRect.minX + cornerRadius, y: drawRect.minY + cornerRadius), radius: cornerRadius, startAngle: .pi, endAngle: .pi * 1.5, clockwise: true) context.strokePath() } }
使用方式
可以直接在Storyboard里把视图类改成PencilBorderView,或者代码创建:
let pencilView = PencilBorderView(frame: CGRect(x: 50, y: 50, width: 200, height: 150)) pencilView.lineWidth = 2.0 pencilView.borderColor = .darkGray pencilView.layer.cornerRadius = 10 view.addSubview(pencilView)
优化小技巧
- 调整随机偏移的范围(比如
-1.0...1.0):数值越大,抖动越明显,手绘感越强;数值越小,边框越规整。 - 用纹理颜色增强真实感:可以把
borderColor换成带铅笔纹理的图案颜色,比如UIColor(patternImage: UIImage(named: "pencilTexture")!),记得纹理图要支持平铺。 - 适配圆角:如果视图有圆角,一定要在路径生成时同步处理圆角部分,不然边框会和视图圆角错位。
内容的提问来源于stack exchange,提问作者Kani Lana
相关产品推荐
相关产品推荐

