我在运行时在代码中添加了一个 UIView
。
我想在其中绘制一个 UIBezierPath
,但这是否意味着我必须覆盖 UIView 的 drawRect
?
或者在定制的UIView
上还有其他的绘制方式吗?
这里是生成UIView
的代码:
UIView* shapeView = [[UIView alloc]initWithFrame:CGRectMake(xOrigin,yOrigin+(i*MENU_BLOCK_FRAME_HEIGHT), self.shapeScroll.frame.size.width, MENU_BLOCK_FRAME_HEIGHT)];
shapeView.clipsToBounds = YES;
这里是创建和返回 UIBezierPath
的函数:
- (UIBezierPath*)createPath
{
UIBezierPath* path = [[UIBezierPath alloc]init];
[path moveToPoint:CGPointMake(100.0, 50.0)];
[path addLineToPoint:CGPointMake(200.0,50.0)];
[path addLineToPoint:CGPointMake(200.0, 200.0)];
[path addLineToPoint:CGPointMake(100.0, 200.0)];
[path closePath];
return path;
}
最佳答案
不久前,我什至不知道如何发音 Bézier,更不用说如何使用 Bézier 路径来制作自定义形状。以下是我学到的。事实证明,它们并不像一开始看起来那么可怕。
如何绘制Bézier path在自定义 View 中
这些是主要步骤:
- 设计所需形状的轮廓。
- 将轮廓路径划分为线段、圆弧段和曲线段。
- 以编程方式构建该路径。
- 在
drawRect
或使用CAShapeLayer
中绘制路径。
设计外形轮廓
你可以做任何事情,但作为一个例子,我选择了下面的形状。它可能是键盘上的弹出键。
将路径分割成段
回顾您的形状设计并将其分解为更简单的线条元素(用于直线)、弧线(用于圆和圆角)和曲线(用于其他任何东西)。
我们的示例设计如下所示:
- 黑色是线段
- 浅蓝色是弧段
- 红色是曲线
- 橙色点是曲线的控制点
- 绿点是路径段之间的点
- 虚线表示边界矩形
- 深蓝色数字是按照程序添加顺序的分段
以编程方式构建路径
我们将任意从左下角开始,顺时针工作。我将使用图像中的网格来获取点的 x 和 y 值。我将在这里对所有内容进行硬编码,但当然你不会在实际项目中这样做。
基本流程是:
- 新建一个
UIBezierPath
- 使用
moveToPoint
选择路径上的起点
- 向路径添加段
- 行:
addLineToPoint
- 圆弧:
addArcWithCenter
- 曲线:
addCurveToPoint
- 行:
- 用
closePath
关闭路径
这是在上图中制作路径的代码。
func createBezierPath() -> UIBezierPath {
// create a new path
let path = UIBezierPath()
// starting point for the path (bottom left)
path.move(to: CGPoint(x: 2, y: 26))
// *********************
// ***** Left side *****
// *********************
// segment 1: line
path.addLine(to: CGPoint(x: 2, y: 15))
// segment 2: curve
path.addCurve(to: CGPoint(x: 0, y: 12), // ending point
controlPoint1: CGPoint(x: 2, y: 14),
controlPoint2: CGPoint(x: 0, y: 14))
// segment 3: line
path.addLine(to: CGPoint(x: 0, y: 2))
// *********************
// ****** Top side *****
// *********************
// segment 4: arc
path.addArc(withCenter: CGPoint(x: 2, y: 2), // center point of circle
radius: 2, // this will make it meet our path line
startAngle: CGFloat(M_PI), // π radians = 180 degrees = straight left
endAngle: CGFloat(3*M_PI_2), // 3π/2 radians = 270 degrees = straight up
clockwise: true) // startAngle to endAngle goes in a clockwise direction
// segment 5: line
path.addLine(to: CGPoint(x: 8, y: 0))
// segment 6: arc
path.addArc(withCenter: CGPoint(x: 8, y: 2),
radius: 2,
startAngle: CGFloat(3*M_PI_2), // straight up
endAngle: CGFloat(0), // 0 radians = straight right
clockwise: true)
// *********************
// ***** Right side ****
// *********************
// segment 7: line
path.addLine(to: CGPoint(x: 10, y: 12))
// segment 8: curve
path.addCurve(to: CGPoint(x: 8, y: 15), // ending point
controlPoint1: CGPoint(x: 10, y: 14),
controlPoint2: CGPoint(x: 8, y: 14))
// segment 9: line
path.addLine(to: CGPoint(x: 8, y: 26))
// *********************
// **** Bottom side ****
// *********************
// segment 10: line
path.close() // draws the final line to close the path
return path
}
注意:上面的一些代码可以通过在单个命令中添加一条线和一条弧来减少(因为弧有一个隐含的起点)。见 here了解更多详情。
画出路径
我们可以在图层或drawRect
中绘制路径。
方法一:在图层中绘制路径
我们的自定义类如下所示。当 View 初始化时,我们将 Bezier 路径添加到新的 CAShapeLayer
。
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() {
// Create a CAShapeLayer
let shapeLayer = CAShapeLayer()
// The Bezier path that we made needs to be converted to
// a CGPath before it can be used on a layer.
shapeLayer.path = createBezierPath().cgPath
// apply other properties related to the path
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = UIColor.white.cgColor
shapeLayer.lineWidth = 1.0
shapeLayer.position = CGPoint(x: 10, y: 10)
// add the new layer to our custom view
self.layer.addSublayer(shapeLayer)
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
然后像这样在 View Controller 中创建我们的 View
override func viewDidLoad() {
super.viewDidLoad()
// create a new UIView and add it to the view controller
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
方法二:draw
使用 draw
比绘制到图层要慢,所以如果不需要,不推荐使用。
这是我们自定义 View 的修改代码:
import UIKit
class MyCustomView: UIView {
override func draw(_ rect: CGRect) {
// create path (see previous code)
let path = createBezierPath()
// fill
let fillColor = UIColor.white
fillColor.setFill()
// stroke
path.lineWidth = 1.0
let strokeColor = UIColor.blue
strokeColor.setStroke()
// Move the path to a new location
path.apply(CGAffineTransform(translationX: 10, y: 10))
// fill and stroke the path (always do these last)
path.fill()
path.stroke()
}
func createBezierPath() -> UIBezierPath {
// see previous code for creating the Bezier path
}
}
这给了我们相同的结果...
进一步研究
我真的建议查看以下 Material 。它们最终使我可以理解贝塞尔路径。 (并教我如何发音:/ˈbɛ zi eɪ/。)
- Thinking like a Bézier path (我从这位作者那里读到的一切都很好,我上面例子的灵感来自这里。)
- Coding Math: Episode 19 - Bezier Curves (有趣且良好的视觉插图)
- Bezier Curves (它们在图形应用程序中的使用方式)
- Bezier Curves (很好地描述了数学公式是如何推导出来的)
关于ios - 在代码生成的 UIView 上绘制 UIBezierPath,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21311880/