ios - 在代码生成的 UIView 上绘制 UIBezierPath

标签 ios iphone objective-c 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];
    [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;
}

最佳答案

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

如何绘制Bézier path在自定义 View 中

这些是主要步骤:

  1. 设计所需形状的轮廓。
  2. 将轮廓路径划分为线段、圆弧段和曲线段。
  3. 以编程方式构建该路径。
  4. drawRect 或使用 CAShapeLayer 中绘制路径。

设计外形轮廓

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

enter image description here

将路径分割成段

回顾您的形状设计并将其分解为更简单的线条元素(用于直线)、弧线(用于圆和圆角)和曲线(用于其他任何东西)。

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

enter image description here

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

以编程方式构建路径

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

基本流程是:

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

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

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
}

注意:上面的一些代码可以通过在单个命令中添加一条线和一条弧来减少(因为弧有一个隐含的起点)。见 here了解更多详情。

画出路径

我们可以在图层或drawRect中绘制路径。

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

我们的自定义类如下所示。当 View 初始化时,我们将 Bezier 路径添加到新的 CAShapeLayer

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
    }
}

然后像这样在 View Controller 中创建我们的 View

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)

}

我们得到...

enter image description here

嗯,这有点小,因为我硬编码了所有数字。不过,我可以放大路径大小,如下所示:

let path = createBezierPath()
let scale = CGAffineTransform(scaleX: 2, y: 2)
path.apply(scale)
shapeLayer.path = path.cgPath

enter image description here

方法二:draw

中绘制路径

使用 draw 比绘制到图层要慢,所以如果不需要,不推荐使用。

这是我们自定义 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
    }
}

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

enter image description here

进一步研究

真的建议查看以下 Material 。它们最终使我可以理解贝塞尔路径。 (并教我如何发音:/ˈbɛ zi eɪ/。)

关于ios - 在代码生成的 UIView 上绘制 UIBezierPath,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21311880/

相关文章:

Objective-C 类型推断

ios - 如何注销 FBWebDialogs?

iphone - 我可以用 Objective-C 以外的任何语言编写 iPhone 应用程序吗?

iphone - iOS - "NSObjectFromString"--- 从字符串构建对象的名称

ios - 获取 UILabels 图像

ios - 如何求曲线和直线的交点?

ios - 如何禁止 NSObject 中的基本 init 方法

ios - 在按Return或Done键后将文本保存在UITextField上

ios - UIToolbar 标准尺寸

iphone - 点击覆盖时显示标注