在代码生成的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
这些是主要步骤:
- devise你想要的形状的轮廓。
- 将轮廓path划分为直线,圆弧和曲线。
- 以编程方式构build该path。
- 使用
drawRect
或使用CAShapeLayer
绘制path。
devise形状轮廓
你可以做任何事情,但作为一个例子,我select了下面的形状。 它可能是一个键盘上的popup键。
将path分成几部分
回头看看你的形状devise,并将其分解成简单的直线(直线),圆弧(圆和圆angular)和曲线(任何其他)。
以下是我们的示例devise:
- 黑色是线段
- 淡蓝色是弧段
- 红色是曲线
- 橙色点是曲线的控制点
- 绿点是path段之间的点
- 虚线显示边界矩形
- 深蓝色数字是按照编程方式添加的顺序
以编程方式构buildpath
我们将任意从左下angular开始顺时针旋转。 我将使用图像中的网格来获取点的x和y值。 我将在这里硬编码一切,但当然你不会在一个真正的项目中这样做。
基本过程是:
- 创build一个新的
UIBezierPath
- 使用
moveToPoint
selectpath的moveToPoint
- 将段添加到path
- 行:
addLineToPoint
- arc:
addArcWithCenter
- 曲线:
addCurveToPoint
- 行:
- 用
closePath
closurespath
这里是上面的图像中的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,这基本上是一个自定义的视图画在屏幕上的东西,如线条,图像,矩形。