ios 在代码生成的 UIView 上绘制 UIBezierPath
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21311880/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Drawing UIBezierPath on code generated UIView
提问by Itzik984
I have a UIView
added in code at run time.
我UIView
在运行时添加了代码。
I want to draw a UIBezierPath
in it, but does this means i have to override the drawRect
for UIView?
我想在里面画一个UIBezierPath
,但这是否意味着我必须覆盖drawRect
UIView ?
Or is there another way of drawing to it on the custom made UIView
?
还是有另一种在定制上绘制的方法UIView
?
Here is the code for generating the 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;
And here is the function to create and return a UIBezierPath
:
这是创建和返回 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;
}
回答by Suragch
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.
不久前,我什至不知道如何发音 Bézier,更不用说如何使用 Bézier 路径来制作自定义形状。以下是我所学到的。事实证明,它们并不像起初看起来那么可怕。
How to draw a Bézier pathin 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
.
- 设计您想要的形状的轮廓。
- 将轮廓路径分成直线、圆弧和曲线段。
- 以编程方式构建该路径。
- 绘制路径无论是在
drawRect
或使用CAShapeLayer
。
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.
我们将任意从左下角开始并顺时针工作。我将使用图像中的网格来获取点的 x 和 y 值。我将在这里对所有内容进行硬编码,但当然您不会在实际项目中这样做。
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
- 创建一个新的
UIBezierPath
- 选择路径上的起点
moveToPoint
- 将段添加到路径
- 线:
addLineToPoint
- 弧:
addArcWithCenter
- 曲线:
addCurveToPoint
- 线:
- 关闭路径
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 herefor more details.
注意:上面的一些代码可以通过在单个命令中添加一条线和一个弧来减少(因为弧有一个隐含的起点)。请参阅此处了解更多详情。
Draw the path
绘制路径
We can draw the path either in a layer or in drawRect
.
我们可以在图层中或在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.
我们的自定义类看起来像这样。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() {
// 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
方法二:在里面绘制路径 draw
Using draw
is slower than drawing to the layer, so this is not the recommended method if you don't need it.
使用draw
比绘制到图层要慢,所以如果你不需要它,这不是推荐的方法。
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 reallyrecommend 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?/.)
我真的建议您查看以下材料。它们最终使我可以理解贝塞尔路径。(并教我如何发音:/?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)
- 像贝塞尔路径一样思考(我从这位作者那里读到的一切都很好,上面例子的灵感来自这里。)
- 编码数学:第 19 集 - 贝塞尔曲线(有趣且良好的视觉插图)
- 贝塞尔曲线(如何在图形应用程序中使用它们)
- 贝塞尔曲线(很好地描述了数学公式是如何推导出来的)
回答by Rui Peres
It would be easier if you would use a CAShapeLayer
, like this:
如果您使用 a 会更容易CAShapeLayer
,如下所示:
CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];
And set its path
:
并设置其path
:
[shapeView setPath:[self createPath].CGPath];
Finally add it:
最后添加:
[[self.view layer] addSublayer:shapeView];
回答by Fogmeister
You can use a CAShapeLayer
to do this.
您可以使用 aCAShapeLayer
来执行此操作。
Like this...
像这样...
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [self createPath].CGPath;
shapeLayer.strokeColor = [UIColor redColor].CGColor; //etc...
shapeLayer.lineWidth = 2.0; //etc...
shapeLayer.position = CGPointMake(100, 100); //etc...
[self.layer addSublayer:shapeLayer];
This will then add and draw the path without having to override drawRect
.
然后,这将添加和绘制路径而无需覆盖drawRect
.
回答by Travis
There are multiple ways to accomplish what you wish. The ones I've seen most are: override drawRect, draw your shape into a CAShapeLayerand then add it as a sublayer to your view, or draw your path onto another context, save that out as an image, and then add it to your view.
有多种方法可以实现您的愿望。我见过的最多的是:覆盖 drawRect,将您的形状绘制到CAShapeLayer 中,然后将其作为子图层添加到您的视图中,或者将您的路径绘制到另一个上下文中,将其另存为图像,然后将其添加到您的看法。
All of these are reasonable choices, and which one is best depends on many other factors such as are you going to be continually adding shapes, how often it's called, etc.
所有这些都是合理的选择,哪一个最好取决于许多其他因素,例如您是否会不断添加形状,调用它的频率等。
回答by Duncan C
As the other posters pointed out, using a shape layer is a good way to go.
正如其他海报指出的那样,使用形状图层是一个很好的方法。
Shape layers a are likely to give you better performance than overriding drawRect.
与覆盖 drawRect 相比,形状图层 a 可能会为您提供更好的性能。
If you want to draw your path yourself then yes, you need to override drawRect for your custom view class.
如果您想自己绘制路径,那么是的,您需要为您的自定义视图类覆盖 drawRect。
回答by Lithu T.V
Yes , You have to override the drawrect if you want to draw anything.Creating a UIBezierPath can be done anywhere ,But to draw something you have to do it inside the drawrect
method
是的,如果你想绘制任何东西,你必须覆盖 drawrect。创建一个 UIBezierPath 可以在任何地方完成,但是要绘制一些东西,你必须在drawrect
方法中做
You should be calling setNeedsDisplay
if you override drawRect in a subclass of UIView which is basically a custom view drawing something on the screen, like lines,image , rectangle.
setNeedsDisplay
如果你在 UIView 的子类中覆盖 drawRect,你应该调用它,它基本上是一个在屏幕上绘制一些东西的自定义视图,比如线、图像、矩形。
回答by jayprakash
Drawing UIBezierPath on code generated UIView, you can use UIView touch events as below. Create global variable for Touch start point and Touch end Point as below:
在代码生成的 UIView 上绘制 UIBezierPath,您可以使用 UIView 触摸事件,如下所示。为触控起点和触控终点创建全局变量,如下所示:
CGPoint startingPoint;
CGPoint endingPoint;
And then draw UIBezierPath using UIView Touchevents as below:
然后使用 UIView Touchevents 绘制 UIBezierPath 如下:
/*Touch Start*/
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
startingPoint = [touch locationInView:self];
}
/*Touch End*/
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
/*Touch Move*/
-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
endingPoint = [touch locationInView:self];
[self makeLineLayer:self.layer lineFromPointA:startingPoint
toPointB:endingPoint];
}
/*Draw UIBezierPath*/
-(void)makeLineLayer:(CALayer *)layer lineFromPointA:(CGPoint)pointA
toPointB:(CGPoint)pointB
{
CAShapeLayer *line = [CAShapeLayer layer];
UIBezierPath *linePath=[UIBezierPath bezierPath];
[linePath moveToPoint: pointA];// Start Point
[linePath addLineToPoint:pointB];//End Point
line.path=linePath.CGPath;
line.fillColor = nil;
line.opacity = 2.0;
line.lineWidth = 4.0;
line.strokeColor = [UIColor redColor].CGColor;
[layer addSublayer:line];
}