在代码生成的 UIView 上绘制 UIBezierPath [英] Drawing UIBezierPath on code generated UIView

查看:20
本文介绍了在代码生成的 UIView 上绘制 UIBezierPath的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在运行时在代码中添加了一个 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];[路径 moveToPoint:CGPointMake(100.0, 50.0)];[路径 addLineToPoint:CGPointMake(200.0,50.0)];[路径 addLineToPoint:CGPointMake(200.0, 200.0)];[路径 addLineToPoint:CGPointMake(100.0, 200.0)];[路径关闭路径];返回路径;}

解决方案

不久前我什至不知道如何发音 Bézier,更不用说如何使用 Bézier 路径来制作自定义形状了.以下是我所学到的.事实证明,它们并不像最初看起来那么可怕.

如何绘制

将路径分成几段

回顾您的形状设计,将其分解为更简单的线条元素(对于直线)、圆弧(对于圆和圆角)和曲线(对于其他任何事物).

我们的示例设计如下所示:

  • 黑色是线段
  • 浅蓝色是弧段
  • 红色是曲线
  • 橙色点是曲线的控制点
  • 绿点是路径段之间的点
  • 虚线表示边界矩形
  • 深蓝色数字是按程序添加顺序的段

以编程方式构建路径

我们将任意从左下角开始并顺时针工作.我将使用图像中的网格来获取点的 x 和 y 值.我将在这里对所有内容进行硬编码,但您当然不会在实际项目中这样做.

基本流程是:

  1. 创建一个新的UIBezierPath
  2. 使用 moveToPoint
  3. 在路径上选择一个起点
  4. 向路径添加段
    • 行:addLineToPoint
    • 弧:addArcWithCenter
    • 曲线:addCurveToPoint
  5. 使用 closePath
  6. 关闭路径

这是制作上图中路径的代码.

func createBezierPath() ->UIBezierPath {//创建一个新路径让路径 = UIBezierPath()//路径的起点(左下角)path.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(to: CGPoint(x: 0, y: 2))//*********************//****** 顶面 *****//*********************//第 4 段:弧path.addArc(withCenter: CGPoint(x: 2, y: 2),//圆的中心点radius: 2,//这将使它与我们的路径线相交startAngle: CGFloat(M_PI),//π 弧度 = 180 度 = 向左直线endAngle: CGFloat(3*M_PI_2),//3π/2 弧度 = 270 度 = 直线向上顺时针:真)//startAngle 到 endAngle 顺时针方向//第 5 段:线path.addLine(to: CGPoint(x: 8, y: 0))//第 6 段:弧path.addArc(withCenter: CGPoint(x: 8, y: 2),半径:2,startAngle: CGFloat(3*M_PI_2),//直线向上endAngle: CGFloat(0),//0 弧度 = 向右顺时针:真)//*********************//***** 右边 ****//*********************//第 7 段:线path.addLine(to: CGPoint(x: 10, y: 12))//第 8 段:曲线path.addCurve(to: CGPoint(x: 8, y: 15),//结束点controlPoint1: CGPoint(x: 10, y: 14),controlPoint2: CGPoint(x: 8, y: 14))//第 9 段:线path.addLine(to: CGPoint(x: 8, y: 26))//*********************//**** 底部 ****//*********************//第 10 段:线path.close()//绘制最后一条线以关闭路径返回路径}

注意:上面的一些代码可以通过在单个命令中添加一条线和一个弧来减少(因为弧有一个隐含的起点).请参阅

嗯,这有点小,因为我硬编码了所有数字.不过,我可以放大路径大小,就像这样:

let path = createBezierPath()让比例 = CGAffineTransform(scaleX: 2, y: 2)路径.应用(规模)shapeLayer.path = path.cgPath

方法二:在draw

中绘制路径

使用draw比绘制到图层要慢,所以如果你不需要,不推荐使用这种方法.

这是我们自定义视图的修改代码:

导入 UIKit类 MyCustomView: UIView {覆盖 func draw(_ rect: CGRect) {//创建路径(见前面的代码)让路径 = createBezierPath()//充满让 fillColor = UIColor.whitefillColor.setFill()//中风路径.线宽 = 1.0让strokeColor = UIColor.bluestrokeColor.setStroke()//将路径移动到新位置path.apply(CGAffineTransform(translationX: 10, y: 10))//填充和描边路径(总是最后做这些)路径填充()path.stroke()}func createBezierPath() ->UIBezierPath {//查看之前创建贝塞尔路径的代码}}

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

进一步研究

真的推荐查看以下材料.它们最终让我可以理解贝塞尔路径.(并教我如何发音:/ˈbɛ zi eɪ/.)

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:

  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 drawRect or using a 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.

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

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ɪ/.)

这篇关于在代码生成的 UIView 上绘制 UIBezierPath的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆