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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:14:19  来源:igfitidea点击:

Drawing UIBezierPath on code generated UIView

iosiphoneobjective-cuiviewuibezierpath

提问by Itzik984

I have a UIViewadded in code at run time.

UIView在运行时添加了代码。

I want to draw a UIBezierPathin it, but does this means i have to override the drawRectfor UIView?

我想在里面画一个UIBezierPath,但这是否意味着我必须覆盖drawRectUIView ?

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:

这些是主要步骤:

  1. Design the outline of the shape you want.
  2. Divide the outline path into segments of lines, arcs, and curves.
  3. Build that path programmatically.
  4. Draw the path either in drawRector using a CAShapeLayer.
  1. 设计您想要的形状的轮廓。
  2. 将轮廓路径分成直线、圆弧和曲线段。
  3. 以编程方式构建该路径。
  4. 绘制路径无论是在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.

你可以做任何事情,但作为例子,我选择了下面的形状。它可能是键盘上的弹出键。

enter image description here

在此处输入图片说明

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:

这是我们的示例设计的样子:

enter image description here

在此处输入图片说明

  • 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:

基本流程是:

  1. Create a new UIBezierPath
  2. Choose a starting point on the path with moveToPoint
  3. Add segments to the path
    • line: addLineToPoint
    • arc: addArcWithCenter
    • curve: addCurveToPoint
  4. Close the path with closePath
  1. 创建一个新的 UIBezierPath
  2. 选择路径上的起点 moveToPoint
  3. 将段添加到路径
    • 线: addLineToPoint
    • 弧: addArcWithCenter
    • 曲线: addCurveToPoint
  4. 关闭路径 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 CAShapeLayerwhen 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...

我们得到...

enter image description here

在此处输入图片说明

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

enter image description here

在此处输入图片说明

Method 2: Draw path in draw

方法二:在里面绘制路径 draw

Using drawis 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...

这给了我们相同的结果......

enter image description here

在此处输入图片说明

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?/。)

回答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 CAShapeLayerto 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 drawrectmethod

是的,如果你想绘制任何东西,你必须覆盖 drawrect。创建一个 UIBezierPath 可以在任何地方完成,但是要绘制一些东西,你必须在drawrect方法中做

You should be calling setNeedsDisplayif 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];
 }