在代码生成的UIView上绘制UIBezierPath

我在运行时在代码中添加了UIView

我想绘制一个UIBezierPath ,但是这意味着我不得不重写drawRect for UIView?

或者是有另一种方式来绘制到自定义的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; 

这里是创build和返回一个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; } 

不久之前,我甚至不知道如何发音贝塞尔,更不用说知道如何使用贝塞尔path来定制形状。 以下是我所学到的。 事实certificate,他们并不像起初看起来那么可怕。

如何在自定义视图中绘制贝塞尔path

这些是主要步骤:

  1. devise你想要的形状的轮廓。
  2. 将轮廓path划分为直线,圆弧和曲线。
  3. 以编程方式构build该path。
  4. 使用drawRect或使用CAShapeLayer绘制path。

devise形状轮廓

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

在这里输入图像描述

将path分成几部分

回头看看你的形状devise,并将其分解成简单的直线(直线),圆弧(圆和圆angular)和曲线(任何其他)。

以下是我们的示例devise:

在这里输入图像描述

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

以编程方式构buildpath

我们将任意从左下angular开始顺时针旋转。 我将使用图像中的网格来获取点的x和y值。 我将在这里硬编码一切,但当然你不会在一个真正的项目中这样做。

基本过程是:

  1. 创build一个新的UIBezierPath
  2. 使用moveToPointselectpath的moveToPoint
  3. 将段添加到path
    • 行: addLineToPoint
    • arc: addArcWithCenter
    • 曲线: addCurveToPoint
  4. closePathclosurespath

这里是上面的图像中的path的代码。

 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 } 

注意:通过在单个命令中添加一条直线和一条弧线(因为弧线具有隐含的起点),可以减less上述代码中的一部分。 在这里看到更多的细节。

绘制path

我们可以在图层或drawRect绘制path。

方法1:在图层中绘制path

我们的自定义类看起来像这样。 初始化视图时,我们将Bezierpath添加到新的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 } } 

并在这样的视图控制器中创build我们的视图

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

我们得到…

在这里输入图像描述

嗯,这有点小,因为我硬编码所有的数字。我可以缩放path的大小,但是,像这样:

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

在这里输入图像描述

方法2:绘制绘制path

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

这给了我们相同的结果…

在这里输入图像描述

进一步研究

真的build议看看下面的材料。 他们终于使Bézierpath对我来说是可以理解的。 (教我如何发音:/bɛzieɪ/。)

  • 像Bézierpath一样思考 (我从这个作者那里读到的所有东西都很好,上面这个例子的灵感来自这里)
  • 编码math:第19集 – 贝塞尔曲线 (娱乐和良好的视觉插图)
  • 贝塞尔曲线 (它们如何在graphics应用中使用)
  • 贝塞尔曲线 (对math公式的推导很好的描述)

如果你使用一个CAShapeLayer ,就像这样:

 CAShapeLayer *shapeView = [[CAShapeLayer alloc] init]; 

并设定其path

 [shapeView setPath:[self createPath].CGPath]; 

最后添加它:

 [[self.view layer] addSublayer:shapeView]; 

您可以使用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]; 

这将添加并绘制path,而不必重写drawRect

有多种方法可以完成你想要的。 我见过的最多的是:覆盖drawRect,把你的形状绘制成一个CAShapeLayer ,然后把它作为一个子图层添加到你的视图中,或者将你的path绘制到另一个上下文中 ,保存为一个图像,然后将其添加到你的视图。

所有这些都是合理的select,哪一个最好取决于许多其他因素,例如你会不断增加形状,多久调用一次等等。

正如其他海报指出的,使用形状层是一个很好的方法。

形状图层a可能会比覆盖drawRect更好的性能。

如果你想自己绘制你的path,那么是的,你需要覆盖你的自定义视图类的drawRect。

是的,如果你想绘制任何东西,你必须重写drawable。创build一个UIBezierPath可以在任何地方完成,但是绘制一些你必须在drawrect方法

你应该调用setNeedsDisplay如果你重写UIView的子类drawRect,这基本上是一个自定义的视图画在屏幕上的东西,如线条,图像,矩形。