在代码生成的UIView上绘制UIBezierPath [英] Drawing UIBezierPath on code generated UIView
问题描述
我在运行时在代码中添加了 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 $ c的函数$ c>:
- (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)];
[路径addLineToPoint:CGPointMake(100.0,200.0)];
[path closePath];
返回路径;
}
不久前我甚至不知道如何发音Bézier,更不用说知道如何使用Bézier路径来制作自定义形状。以下是我所学到的。事实证明,它们并不像最初看起来那么可怕。
如何绘制
将路径划分为段
回顾一下你的形状设计并将其分解成更简单的线条元素(直线),圆弧(圆形和圆角)和曲线(其他任何东西)。
这是我们的示例设计的样子:
< a href =https://i.stack.imgur.com/4mkcI.png =noreferrer>
- 黑色是线段
- 浅蓝色是弧段
- 红色是曲线
- 橙色圆点是曲线的控制点
- 绿点是路径段之间的点
- 虚线显示边界矩形
- 深蓝色数字是按编程方式添加的顺序段
以编程方式构建路径
我们将在左下角任意开始并按顺时针方向工作。我将使用图像中的网格来获取点的x和y值。我会在这里对所有内容进行硬编码,但当然你不会在一个真实的项目中这样做。
基本过程是:
- 创建新的
UIBezierPath
- 选择路径上的起点
moveToPoint
- 将路段添加到路径
- line:
addLineToPoint
- arc:
addArcWithCenter
- 曲线:
addCurveToPoint
- line:
- 使用
关闭路径closePath
以下是在上图中制作路径的代码。
func createBezierPath() - > UIBezierPath {
//创建一个新路径
let path = UIBezierPath()
//路径起点(左下角)
路径.move(to:CGPoint(x:2,y:26))
// *********************
// *****左侧*****
// *********************
//段1:行
path.addLine(to:CGPoint(x:2,y:15))
//段2:曲线
path.addCurve(to :CGPoint(x:0,y:12),//结束点
controlPoint1:CGPoint(x:2,y:14),
controlPoint2:CGPoint(x:0,y:14))
//段3:行
path.addLine(至:CGPoint(x:0,y:2))
// ***** ****************
// ******上方*****
// ********* ************
//段4:arc
path.addArc(withCenter:CGPoint(x:2,y:2),// center圆点
半径:2,//这将使它符合我们的路径行
startAngle:CGFloat(M_PI),//π弧度= 180度=直线左边
endAngle:CGFloat(3 * M_PI_2),//3π/ 2弧度= 270度=直线上升
顺时针:真)// startAngle到endAngle顺时针方向
//段5:行
path.addLine(至:CGPoint(x:8,y:0))
//段6:arc
path.addArc(withCenter:CGPoint(x:8,y:2),
radius:2,
startAngle:CGFloat(3 * M_PI_2),//直接上涨
endAngle: CGFloat(0),// 0弧度=直右
顺时针:真)
// ******************** *
// *****右侧****
// *********************
//段7:行
path.addLine(to:CGPoint(x:10,y:12))
//段8:曲线
路径。 addCurve(to:CGPoint(x:8,y:15),//结束点
controlPoint1:CGPoint(x:10,y:14),
controlPoint2:CGPoint(x:8,y: 14))
// segm第9行:行
path.addLine(至:CGPoint(x:8,y:26))
// *************** ******
// ****底边****
// *********************
//段10:行
path.close()//绘制最后一行以关闭路径
返回路径
}
注意:通过在单个命令中添加一条线和一条弧可以减少上面的一些代码(因为arc有一个隐含的起点)。有关详细信息,请参见
嗯,这有点小,因为我硬编了所有的数字。我可以扩大路径大小,但是,像这样:
let path = createBezierPath()
let scale = CGAffineTransform(scaleX:2 ,y:2)
path.apply(scale)
shapeLayer.path = path.cgPath
方法2:在中绘制路径
使用 draw
比绘制到图层要慢,所以这不是如果你不需要它,推荐的方法。
以下是我们自定义视图的修订代码:
import UIKit
class MyCustomView:UIView {
override func draw(_ rect:CGRect){
// create path(参见前面的代码)
let path = createBezierPath()
// fill
let fillColor = UIColor.white
fillColor.setFill()
// stroke
path.lineWidth = 1.0
let strokeColor = UIColor.blue
strokeColor.setStroke()
//将路径移动到新位置
path.apply(CGAffineTransform(translationX:10,y:10))
//填充并描边路径(总是最后这些)
path.fill()
path.stroke()
}
func createBezierPath() - > UIBezierPath {
//查看以前创建Bezier路径的代码
}
}
给了我们相同的结果......
进一步研究
我确实建议查看以下资料。他们最终使Bézier的道路对我来说是可以理解的。 (并告诉我如何发音:/bɛzi.eɪ/.)
- 像Bézier路径一样思考(我从这位作者那里读到的一切都很好,上面例子的灵感来自这里。 )
- 编码数学:第19集 - 贝塞尔曲线(娱乐和良好的视觉插图)
- Bezier Curves (如何在图形应用程序中使用它们)
- Bezier曲线(如何推导出数学公式的良好描述)
I have a UIView
added in code at run time.
I want to draw a UIBezierPath
in it, but does this means i have to override the drawRect
for UIView?
Or is there another way of drawing to it on the custom made UIView
?
Here is the code for generating the 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;
And here is the function to create and return a 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;
}
It wasn't long ago that I didn't even know how to pronounce Bézier, let alone know how to use Bézier paths to make a custom shape. The following is what I have learned. It turns out that they aren't as scary as they seem at first.
How to draw a Bézier path in a custom view
These are the main steps:
- Design the outline of the shape you want.
- Divide the outline path into segments of lines, arcs, and curves.
- Build that path programmatically.
- Draw the path either in
drawRect
or using aCAShapeLayer
.
Design shape outline
You could do anything, but as an example I have chosen the shape below. It could be a popup key on a keyboard.
Divide the path into segments
Look back at your shape design and break it down into simpler elements of lines (for straight lines), arcs (for circles and round corners), and curves (for anything else).
Here is what our example design would look like:
- Black are line segments
- Light blue are arc segments
- Red are curves
- Orange dots are the control points for the curves
- Green dots are the points between path segments
- Dotted lines show the bounding rectangle
- Dark blue numbers are the segments in the order that they will be added programmatically
Build the path programmatically
We'll arbitrarily start in the bottom left corner and work clockwise. I'll use the grid in the image to get the x and y values for the points. I'll hardcode everything here, but of course you wouldn't do that in a real project.
The basic process is:
- Create a new
UIBezierPath
- Choose a starting point on the path with
moveToPoint
- Add segments to the path
- line:
addLineToPoint
- arc:
addArcWithCenter
- curve:
addCurveToPoint
- line:
- Close the path with
closePath
Here is the code to make the path in the image above.
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
}
Note: Some of the above code can be reduced by adding a line and an arc in a single command (since the arc has an implied starting point). See here for more details.
Draw the path
We can draw the path either in a layer or in drawRect
.
Method 1: Draw path in a layer
Our custom class looks like this. We add our Bezier path to a new CAShapeLayer
when the view is initialized.
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
}
}
And creating our view in the View Controller like this
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)
}
We get...
Hmm, that's a little small because I hardcoded all the numbers in. I can scale the path size up, though, like this:
let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath
Method 2: Draw path in draw
Using draw
is slower than drawing to the layer, so this is not the recommended method if you don't need it.
Here is the revised code for our custom 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
}
}
which gives us the same result...
Further study
I really recommend looking at the following materials. They are what finally made Bézier paths understandable for me. (And taught me how to pronounce it: /ˈbɛ zi eɪ/.)
- Thinking like a Bézier path (Everything I've ever read from this author is good and the inspiration for my example above came from here.)
- Coding Math: Episode 19 - Bezier Curves (entertaining and good visual illustrations)
- Bezier Curves (how they are used in graphics applications)
- Bezier Curves (good description of how the mathematical formulas are derived)
这篇关于在代码生成的UIView上绘制UIBezierPath的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!