不久前,我什至不知道如何发音贝塞尔(Bézier),更不用说知道如何使用贝塞尔(Bézier)路径制作自定义形状了。以下是我所学到的。事实证明,它们并不像最初看起来的那样可怕。
如何在自定义视图中绘制贝塞尔路径
这些是主要步骤:
- 设计所需形状的轮廓。
- 将轮廓路径分为直线,圆弧和曲线段。
- 以编程方式构建该路径。
drawRect
使用或使用绘制路径CAShapeLayer
。
设计形状轮廓
您可以做任何事情,但是作为示例,我选择了以下形状。它可能是键盘上的弹出键。
将路径分为多个部分
回顾一下您的形状设计,并将其分解为线(对于直线),弧(对于圆和圆角)和曲线(对于其他任何东西)的简单元素。
这是我们的示例设计的样子:
- 黑色是线段
- 浅蓝色是弧线段
- 红色是曲线
- 橙色点是曲线的控制点
- 绿点是路径段之间的点
- 虚线显示边界矩形
- 深蓝色数字是按程序段顺序添加的分段
以编程方式构建路径
我们将在左下角任意开始,然后顺时针方向工作。我将使用图像中的网格获取点的x和y值。我将在此处对所有内容进行硬编码,但是您当然不会在实际项目中这样做。
基本过程是:
- 创建一个新的
UIBezierPath
- 选择路径的起点
moveToPoint
- 将细分添加到路径
- 线:
addLineToPoint
- 弧:
addArcWithCenter
- 曲线:
addCurveToPoint
- 用以下方式封闭路径
closePath
这是在上图中创建路径的代码。
func createBezierPath() -> UIBezierPath {
let path = UIBezierPath()
path.move(to: CGPoint(x: 2, y: 26))
path.addLine(to: CGPoint(x: 2, y: 15))
path.addCurve(to: CGPoint(x: 0, y: 12),
controlPoint1: CGPoint(x: 2, y: 14),
controlPoint2: CGPoint(x: 0, y: 14))
path.addLine(to: CGPoint(x: 0, y: 2))
path.addArc(withCenter: CGPoint(x: 2, y: 2),
radius: 2,
startAngle: CGFloat(M_PI),
endAngle: CGFloat(3*M_PI_2),
clockwise: true)
path.addLine(to: CGPoint(x: 8, y: 0))
path.addArc(withCenter: CGPoint(x: 8, y: 2),
radius: 2,
startAngle: CGFloat(3*M_PI_2),
endAngle: CGFloat(0),
clockwise: true)
path.addLine(to: CGPoint(x: 10, y: 12))
path.addCurve(to: CGPoint(x: 8, y: 15),
controlPoint1: CGPoint(x: 10, y: 14),
controlPoint2: CGPoint(x: 8, y: 14))
path.addLine(to: CGPoint(x: 8, y: 26))
path.close()
return path
}
注意:可以通过在单个命令中添加一条线和一条圆弧来减少上面的某些代码(因为圆弧有一个隐含的起点)。有关更多详细信息,请参见此处。
画出道路
我们可以在图层或图层中绘制路径drawRect
。
方法1:在图层中绘制路径
我们的自定义类如下所示。CAShapeLayer
初始化视图后,我们将Bezier路径添加到新路径。
import UIKit
class MyCustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
let shapeLayer = CAShapeLayer()
shapeLayer.path = createBezierPath().cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = CGPoint(x: 10, y: 10)
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {
}
}
然后像这样在视图控制器中创建视图
override func viewDidLoad() {
super.viewDidLoad()
let myView = MyCustomView()
myView.frame = CGRect(x: 100, y: 100, width: 50, height: 50)
myView.backgroundColor = UIColor.yellow
view.addSubview(myView)
}
我们得到...
嗯,这有点小,因为我将所有数字都硬编码了。不过,我可以像下面这样放大路径大小:
let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath
方法2:在中绘制路径 draw
使用draw
的速度比绘制图层的速度慢,因此如果不需要,则不建议使用此方法。
这是我们的自定义视图的修改后的代码:
import UIKit
class MyCustomView: UIView {
override func draw(_ rect: CGRect) {
let path = createBezierPath()
let fillColor = UIColor.white
fillColor.setFill()
path.lineWidth = 1.0
let strokeColor = UIColor.blue
strokeColor.setStroke()
path.apply(CGAffineTransform(translationX: 10, y: 10))
path.fill()
path.stroke()
}
func createBezierPath() -> UIBezierPath {
}
}
这给了我们相同的结果...
进一步研究
我真的建议您查看以下材料。它们终于使贝塞尔(Bézier)道路对我而言可以理解。(并教我如何发音:/ ˈbɛzieɪ/。)
frame
视图改变了怎么办?当方向发生变化时,我们如何调整形状的大小?