在代码生成的 UIView 上绘制 UIBezierPath

我在运行时添加了一个 UIView代码。

我想在它画一个 UIBezierPath,但这是否意味着我必须覆盖 drawRect为 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;

下面是创建和返回 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;
}
80892 次浏览

如果你使用 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];

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

是的,如果你想画任何东西,你必须覆盖抽签。创建 UIBezierPath 可以在任何地方进行,但是要绘制某些内容,必须在 drawrect方法中进行

如果在 UIView 的子类中覆盖 draRect,那么应该调用 setNeedsDisplay,UIView 基本上是一个自定义视图,在屏幕上绘制一些东西,比如线条、图像、矩形。

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

形状图层可能会给你更好的性能比覆盖 Drarect。

如果您想自己绘制路径,那么是的,您需要为自定义视图类重写 draRect。

有很多方法可以实现你的愿望。我见过的最多的是: 覆盖 draRect,把你的形状画成一个 CAShapeLayer,然后把它作为一个子层添加到你的视图,或者 把你的路线画到另一个背景上,保存为一个图像,然后把它添加到你的视图。

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

就在不久前,我甚至不知道如何发音,更不用说知道如何使用贝塞尔路径,使一个定制的形状。以下是我所学到的。事实证明他们并不像一开始看起来那么可怕。

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

以下是主要步骤:

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

设计外形轮廓

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

enter image description here

把路分成几段

回头看看你的形状设计,把它分解成更简单的元素: 直线(用于直线)、弧线(用于圆形和圆角)和曲线(用于其他任何东西)。

下面是我们的示例设计:

enter image description here

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

以编程方式生成路径

我们从左下角开始,顺时针方向工作。我将使用图像中的网格来获取点的 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
}

注意: 上面的一些代码可以通过在单个命令中添加一行和一个弧来减少(因为弧有一个隐含的起点)。有关详细信息,请参阅 给你

画出路线

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

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

我们的自定义类如下所示。当视图初始化时,我们将 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
}
}

然后像这样在视图控制器中创建我们的视图

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

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

结果是一样的。

enter image description here

进一步研究

真的建议看看下面的材料。它们最终让我理解了贝塞尔的道路。(还教我怎么发音:/b zi e/)

在代码生成的 UIView 上绘制 UIBezierPath,可以使用 UIView 触摸事件,如下所示。为触摸起点和触摸终点创建全局变量,如下所示:

CGPoint startingPoint;
CGPoint endingPoint;

然后使用 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];
}