在代码生成的UIView上绘制UIBezierPath

Itz*_*984 63 iphone objective-c uiview ios uibezierpath

UIView在运行时添加了代码.

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

或者是否有其他方式在定制上绘制它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;
Run Code Online (Sandbox Code Playgroud)

以下是创建和返回的函数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;
}
Run Code Online (Sandbox Code Playgroud)

Sur*_*gch 234

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

如何在自定义视图中绘制Bézier路径

这些是主要步骤:

  1. 设计您想要的形状轮廓.
  2. 将轮廓路径分为线段,圆弧和曲线.
  3. 以编程方式构建该路径.
  4. drawRect或使用a 绘制路径CAShapeLayer.

设计形状轮廓

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

在此输入图像描述

将路径划分为多个部分

回顾一下你的形状设计并将其分解成更简单的线条元素(用于直线),弧形(用于圆形和圆角)和曲线(用于其他任何东西).

以下是我们的示例设计:

在此输入图像描述

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

以编程方式构建路径

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

基本过程是:

  1. 创建一个新的 UIBezierPath
  2. 选择路径上的起点 moveToPoint
  3. 将段添加到路径中
    • 线: addLineToPoint
    • 弧: addArcWithCenter
    • 曲线: addCurveToPoint
  4. 关闭路径 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
}
Run Code Online (Sandbox Code Playgroud)

注意:通过在单个命令中添加直线和圆弧可以减少上述代码中的一些(因为圆弧具有隐含的起点).有关详细信息,请参见此处

画出路径

我们可以在一层或一层中绘制路径drawRect.

方法1:在图层中绘制路径

我们的自定义类看起来像这样.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
    }
}
Run Code Online (Sandbox Code Playgroud)

并在View Controller中创建我们的视图

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)

}
Run Code Online (Sandbox Code Playgroud)

我们得到......

在此输入图像描述

嗯,这有点小,因为我硬编码所有的数字.我可以扩大路径大小,但是,像这样:

let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

方法2:绘制路径 draw

使用draw比绘制到图层要慢,因此如果您不需要,这不是推荐的方法.

以下是我们自定义视图的修订代码:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

进一步研究

真的建议看下面的材料.他们最终让Bézier的道路对我来说是可以理解的.(并告诉我如何发音:/bɛzi.eɪ/.)

  • @ozgur,至少有两个选择。一种方法是进行缩放(可能还有平移)变换,如我在上面的示例中所示。另一种选择是根据新框架重新计算贝塞尔路径。在上面的示例中,我将所有数字硬编码到 Bezier 路径中。但是,当我在实际项目中使用 Bezier 路径时,我会根据帧大小确定 Bezier 值。当框架(或更可能是边界)发生变化时,我会重新计算贝塞尔曲线。 (3认同)

Rui*_*res 53

如果您使用a会更容易CAShapeLayer,如下所示:

CAShapeLayer *shapeView = [[CAShapeLayer alloc] init];
Run Code Online (Sandbox Code Playgroud)

并设置path:

[shapeView setPath:[self createPath].CGPath];
Run Code Online (Sandbox Code Playgroud)

最后加上它:

[[self.view layer] addSublayer:shapeView];
Run Code Online (Sandbox Code Playgroud)


Fog*_*ter 15

您可以使用a CAShapeLayer来执行此操作.

像这样...

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];
Run Code Online (Sandbox Code Playgroud)

然后,这将添加并绘制路径,而不必覆盖drawRect.


Tra*_*vis 5

有多种方法可以实现您的目标.我最常见的是:覆盖drawRect,将您的形状绘制到CAShapeLayer中,然后将其作为子图层添加到视图中,或将路径绘制到另一个上下文中,将其另存为图像,然后将其添加到您的视图.

所有这些都是合理的选择,哪一个最好取决于许多其他因素,例如你是否会不断添加形状,调用它的频率等等.