使用UIBezierPath和CAShapeLayer画各种图形
转载自:http://www.cocoachina.com/ios/20160214/15251.html
CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画出各种图形,当然,你也可以使用其他方式来画,随你。
杂谈
在 CAShapeLayer 中,也可以像 CALayer 一样指定它的 frame 来画,就像这样:
1
2
3
4
5
|
let layer = CAShapeLayer() layer.frame = CGRectMake(110, 100, 150, 100) layer.backgroundColor = UIColor.blackColor().CGColor view.layer.addSublayer(layer) |
然后你就会得到如图这样的黑色矩形
但是,CAShapeLayer 有一个神奇的属性 path 用这个属性配合上 UIBezierPath 这个类就可以达到超神的效果。
UIBezierPath 顾名思义,这是用贝塞尔曲线的方式来构建一段弧线,你可以用任意条弧线来组成你想要的形状,比如,你想用它来和上面一样画一个矩形,那就可以这样子来做:
1
2
3
4
5
|
let path = UIBezierPath(rect: CGRectMake(110, 100, 150, 100)) let layer = CAShapeLayer() layer.path = path.CGPath layer.fillColor = UIColor.blackColor().CGColor view.layer.addSublayer(layer) |
要注意的是,这里就不要用backgroundColor 这个属性了,而要使用 fillColor 和 strokeColor ,前者代表设置这个 Layer 的填充色,后者代表设置它的边框色
1
2
|
layer.fillColor = UIColor.clearColor().CGCo lorlayer.strokeColor = UIColor.blackColor().CGColor |
可以试一下把上面代码设置颜色的部分改成这个样子,那么运行程序的时候就会是这种样子
玩一下UIBezierPath
在说回 UIBezierPath ,在 UIBezierPath 的 init 方法中,就有很多方便你画各种图形的方法,比如你可以画一个带圆角的图形
1
2
3
4
5
|
let path = UIBezierPath(roundedRect: CGRectMake(110, 100, 150, 100), cornerRadius: 50) let layer = CAShapeLayer() layer.path = path.CGPath layer.fillColor = UIColor.clearColor().CGColor layer.strokeColor = UIColor.blackColor().CGColor |
还可以指定起始角和半径画圆
1
2
3
4
5
6
7
8
|
let radius: CGFloat = 60.0 let startAngle: CGFloat = 0.0 let endAngle: CGFloat = CGFloat(M_PI * 2) let path = UIBezierPath(arcCenter: view.center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true ) let layer = CAShapeLayer() layer.path = path.CGPath layer.fillColor = UIColor.clearColor().CGColor layer.strokeColor = UIColor.blackColor().CGColor |
在这里涉及到角度的问题,起始角和结束角,这里的角度使使用弧度制来表示,这里我收藏了一张图片,以方便参考。
怎么画曲线
贝塞尔曲线的画法是由起点、终点、控制点三个参数来画的,为了解释清楚这个点,我写了几行代码来解释它
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
let startPoint = CGPointMake(50, 300) let endPoint = CGPointMake(300, 300) let controlPoint = CGPointMake(170, 200) let layer1 = CALayer() layer1.frame = CGRectMake(startPoint.x, startPoint.y, 5, 5) layer1.backgroundColor = UIColor.redColor().CGColor let layer2 = CALayer() layer2.frame = CGRectMake(endPoint.x, endPoint.y, 5, 5) layer2.backgroundColor = UIColor.redColor().CGColor let layer3 = CALayer() layer3.frame = CGRectMake(controlPoint.x, controlPoint.y, 5, 5) layer3.backgroundColor = UIColor.redColor().CGColor let path = UIBezierPath() let layer = CAShapeLayer() path.moveToPoint(startPoint) path.addQuadCurveToPoint(endPoint, controlPoint: controlPoint) layer.path = path.CGPath layer.fillColor = UIColor.clearColor().CGColor layer.strokeColor = UIColor.blackColor().CGColor view.layer.addSublayer(layer) view.layer.addSublayer(layer1) view.layer.addSublayer(layer2) view.layer.addSublayer(layer3) |
我很随意的定义了三个点,为了清楚显示它们的位置,我放了三个矩形在上面以便观察,然后调用 path.moveToPoint(startPoint) 让它移动到起始点,然后调用path.addQuadCurveToPoint(endPoint, controlPoint: controlPoint) 这个方法告诉它结束点和控制点,这样它就能画出一条有弧度的线条了,如果把fillColor设置一个颜色,那么它就会变成一个很丑的形状了,示例图如下
控制点决定了它的曲率,曲线的顶点不等于控制点的位置,具体可以看一下贝塞尔曲线的定义,你还可以使用两个控制点来画,两个控制点可以使用方法 path.addCurveToPoint(endPoint, controlPoint1: controlPoint, controlPoint2: controlPoint2)来搞定
这样它会是这个样子
再来说说 CAShapeLayer
CAShapeLayer 是个神奇的东西,给它一个path它就能变成你想要的形状,它还有很多可以玩的地方。综合使用可以组合成不同的动画,比如下面这样
demo1.gif
demo2.gif
demo3.gif
这三个动画就是使用了 strokeEnd、strokeStart和lineWidth 三个属性,第一个动画用了strokeEnd这个属性的值范围是0-1,动画显示了从0到1之间每一个值对这条曲线的影响,strokeStart的方法则是相反的,如果把这两个值首先都设置成0.5然后慢慢改变成0和1的时候就会变成第二个动画,配合lineWidth则曲线会慢慢变粗,这里的很多属性都是支持动画的。
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
private func animation1() { let animation = CABasicAnimation(keyPath: "strokeEnd" ) animation.fromValue = 0 animation.toValue = 1 animation.duration = 2 layer.addAnimation(animation, forKey: "" ) } private func animation2() { layer.strokeStart = 0.5 layer.strokeEnd = 0.5 let animation = CABasicAnimation(keyPath: "strokeStart" ) animation.fromValue = 0.5 animation.toValue = 0 animation.duration = 2 let animation2 = CABasicAnimation(keyPath: "strokeEnd" ) animation2.fromValue = 0.5 animation2.toValue = 1 animation2.duration = 2 layer.addAnimation(animation, forKey: "" ) layer.addAnimation(animation2, forKey: "" ) } private func animation3() { let animation = CABasicAnimation(keyPath: "lineWidth" ) animation.fromValue = 1 animation.toValue = 10 animation.duration = 2 layer.addAnimation(animation, forKey: "" ) } |
应用一下
前一阵子在仿时光网这个APP,其中有一个Layer的形状很怪异,是这样的
很明显它可以用 CAShapeLayer + UIBezierPath 来做,思路大概是这样,先移动到左上方的位置,然后向下划线,然后往右划线,然后往上划线,还剩一个盖子,这个盖子就用一个控制点控制曲率,非常简单,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
|
let finalSize = CGSizeMake(CGRectGetWidth(view.frame), 400) let layerHeight = finalSize.height * 0.2 let layer = CAShapeLayer() let bezier = UIBezierPath() bezier.moveToPoint(CGPointMake(0, finalSize.height - layerHeight)) bezier.addLineToPoint(CGPointMake(0, finalSize.height - 1)) bezier.addLineToPoint(CGPointMake(finalSize.width, finalSize.height - 1)) bezier.addLineToPoint(CGPointMake(finalSize.width, finalSize.height - layerHeight)) bezier.addQuadCurveToPoint(CGPointMake(0,finalSize.height - layerHeight), controlPoint: CGPointMake(finalSize.width / 2, (finalSize.height - layerHeight) - 40)) layer.path = bezier.CGPath layer.fillColor = UIColor.blackColor().CGColor view.layer.addSublayer(layer) |
就能画出这样的形状来
再来一个复杂点的,微信下拉拍小视频的那只眼睛很有趣,来模仿一下那个效果吧,它是这样的
首先你得画出这只眼睛,这是眼睛包括5个部分组成(这个是用OC写的)
1
2
3
4
5
|
@property (strong, nonatomic) CAShapeLayer *eyeFirstLightLayer; @property (strong, nonatomic) CAShapeLayer *eyeSecondLightLayer; @property (strong, nonatomic) CAShapeLayer *eyeballLayer; @property (strong, nonatomic) CAShapeLayer *topEyesocketLayer; @property (strong, nonatomic) CAShapeLayer *bottomEyesocketLayer; |
然后,还是通过 UIBezierPath 和 CAShapeLayer 这样的老套路来画,代码较多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
- (CAShapeLayer *)eyeFirstLightLayer { if (!_eyeFirstLightLayer) { _eyeFirstLightLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:CGRectGetWidth(self.frame) * 0.2 startAngle:(230.f / 180.f) * M_PI endAngle:(265.f / 180.f) * M_PI clockwise:YES]; _eyeFirstLightLayer.borderColor = [UIColor blackColor].CGColor; _eyeFirstLightLayer.lineWidth = 5.f; _eyeFirstLightLayer.path = path.CGPath; _eyeFirstLightLayer.fillColor = [UIColor clearColor].CGColor; _eyeFirstLightLayer.strokeColor = [UIColor whiteColor].CGColor; } return _eyeFirstLightLayer; } - (CAShapeLayer *)eyeSecondLightLayer { if (!_eyeSecondLightLayer) { _eyeSecondLightLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:CGRectGetWidth(self.frame) * 0.2 startAngle:(211.f / 180.f) * M_PI endAngle:(220.f / 180.f) * M_PI clockwise:YES]; _eyeSecondLightLayer.borderColor = [UIColor blackColor].CGColor; _eyeSecondLightLayer.lineWidth = 5.f; _eyeSecondLightLayer.path = path.CGPath; _eyeSecondLightLayer.fillColor = [UIColor clearColor].CGColor; _eyeSecondLightLayer.strokeColor = [UIColor whiteColor].CGColor; } return _eyeSecondLightLayer; } - (CAShapeLayer *)eyeballLayer { if (!_eyeballLayer) { _eyeballLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:CGRectGetWidth(self.frame) * 0.3 startAngle:(0.f / 180.f) * M_PI endAngle:(360.f / 180.f) * M_PI clockwise:YES]; _eyeballLayer.borderColor = [UIColor blackColor].CGColor; _eyeballLayer.lineWidth = 1.f; _eyeballLayer.path = path.CGPath; _eyeballLayer.fillColor = [UIColor clearColor].CGColor; _eyeballLayer.strokeColor = [UIColor whiteColor].CGColor; _eyeballLayer.anchorPoint = CGPointMake(0.5, 0.5); } return _eyeballLayer; } - (CAShapeLayer *)topEyesocketLayer { if (!_topEyesocketLayer) { _topEyesocketLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(0, CGRectGetHeight(self.frame) / 2)]; [path addQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame) / 2) controlPoint:CGPointMake(CGRectGetWidth(self.frame) / 2, center.y - center.y - 20)]; _topEyesocketLayer.borderColor = [UIColor blackColor].CGColor; _topEyesocketLayer.lineWidth = 1.f; _topEyesocketLayer.path = path.CGPath; _topEyesocketLayer.fillColor = [UIColor clearColor].CGColor; _topEyesocketLayer.strokeColor = [UIColor whiteColor].CGColor; } return _topEyesocketLayer; } - (CAShapeLayer *)bottomEyesocketLayer { if (!_bottomEyesocketLayer) { _bottomEyesocketLayer = [CAShapeLayer layer]; CGPoint center = CGPointMake(CGRectGetWidth(self.frame) / 2, CGRectGetHeight(self.frame) / 2); UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(0, CGRectGetHeight(self.frame) / 2)]; [path addQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame) / 2) controlPoint:CGPointMake(CGRectGetWidth(self.frame) / 2, center.y + center.y + 20)]; _bottomEyesocketLayer.borderColor = [UIColor blackColor].CGColor; _bottomEyesocketLayer.lineWidth = 1.f; _bottomEyesocketLayer.path = path.CGPath; _bottomEyesocketLayer.fillColor = [UIColor clearColor].CGColor; _bottomEyesocketLayer.strokeColor = [UIColor whiteColor].CGColor; } return _bottomEyesocketLayer; } |
然后更改一下某些属性的值,方便稍后的动画
1
2
3
4
5
6
7
8
|
- (void)setupAnimation { self.eyeFirstLightLayer.lineWidth = 0.f; self.eyeSecondLightLayer.lineWidth = 0.f; self.eyeballLayer.opacity = 0.f; _bottomEyesocketLayer.strokeStart = 0.5f; _bottomEyesocketLayer.strokeEnd = 0.5f; _topEyesocketLayer.strokeStart = 0.5f; _topEyesocketLayer.strokeEnd = 0.5f;} |
最后根据 UIScrollView 的 contentOffset 来控制各种属性,办法较笨,但管用。
最后
总之使用 UIbezierPath 和 CAShapeLayer 可以画出你想要的任何形状,没有它做不到,只有你想不到,搞定了它们你就可以轻松定制你想要的任何控件了。
使用UIBezierPath和CAShapeLayer画各种图形的更多相关文章
- 放肆的使用UIBezierPath和CAShapeLayer画各种图形
CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画出各种图形,当然,你也可以使用其他方式来画,随你. 杂谈 在 CAShapeLayer 中,也可以像 CAL ...
- iOS 使用UIBezierPath和CAShapeLayer画各种图形
CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画出各种图形,当然,你也可以使用其他方式来画,随你. 杂谈 在 CAShapeLayer 中,也可以像 CAL ...
- UIBezierPath和CAShapeLayer的关系
CAShapeLayer是基于贝塞尔曲线而存在的, 如果没有贝塞尔曲线提供路径来画出图形, CAShapeLayer就没有存在的意义, CAShapeLayer可以使得不用在drawRect:方法中实 ...
- iOS-CGContextRef画各种图形例子
iOS-CGContextRef画各种图形例子 绘制效果图 绘制代码 - (void)drawRect:(CGRect)rect { //一个不透明类型的Quartz 2D绘画环境,相当于一个画布,你 ...
- UIBezierPath 和 CAShapeLayer 绘画图纸
五角大楼画一个小圆圈戴: - (void)drawPentagon{ //(1)UIBezierPath对象 UIBezierPath *aPath = [UIBezierPath bezierPat ...
- AJ学IOS(29)UI之Quartz2D自定义下载控件_画各种图形
AJ分享,必须精品 效果 自定义控件过程 主要过程在上一篇里有介绍了,这里主要介绍下代码实现 先做好要放的view 然后实现呢主要就是四步: 1:获取上下文 2:拼接路径 3:把路径添加到上下文. 4 ...
- Java界面编程-建立一个可以画出图形的简单框架
引子:总共使用3个.java文件,建立一个简单界面编程的框架. 第1个文件:NotHelloWorldComponent.java //NotHelloWorldComponent.java 1 im ...
- 用CSS画基本图形
用CSS画基本图形 1.正方形 代码如下: #square { width: 100px; height: 100px; background: red; } 2.长方形 代码如下: #recta ...
- python3 使用opencv 画基本图形
在Python3 环境下安装opencv-python 后练习画基本图形: import numpy as np import cv2 # BGR format GREEN = (0, 255, 0) ...
随机推荐
- JavaScript---网络编程(12)--DHTML技术演示(5)-form表单验证技术(正则)
这里不进行很复杂的后台验证以及JavaScript的正则表达式,只是简单的介绍下这个技术,简单的后台接收与跳转,大概了解怎么验证的就可以.具体的技术,我后面还会继续写博客的.本人也还在学习中. 表单验 ...
- JSP九大内置对象详解
内置对象特点: 1. 由JSP规范提供,不用编写者实例化. 2. 通过Web容器实现和管理 3. 所有JSP页面均可使用 4. ...
- Get familiar with key Frameworks of ios
Frameworks make your life easier as an iOS Developer. They allow you to reuse code written by other ...
- 系统内存和CPU管理、监控
本博文的主要内容有 .系统内存管理.监控:vmstat和free -mt .系统CPU管理.监控:sar -u.mpstat.uptime linux系统内存和CPU是在系统运行的过程中不断消耗的资源 ...
- YII安装smarty-view-renderer扩展
smarty-view-renderer http://www.yiiframework.com/extension/smarty-view-renderer/ 相关下载及介绍:https://git ...
- How I Mathematician Wonder What You Are! - POJ 3130(求多边形的核)
题目大意:判断多多边形是否存在内核. 代码如下: #include<iostream> #include<string.h> #include<stdio.h> # ...
- hdoj 2404 Permutation Recovery【逆序对】
Permutation Recovery Time Limit: 10000/4000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Ot ...
- [置顶] 深入理解android之IPC机制与Binder框架
[android之IPC机制与Binder框架] [Binder框架.Parcel.Proxy-Stub以及AIDL] Abstract [每个平台都会有自己一套跨进程的IPC机制,让不同进程里的两个 ...
- jquerymobile知识点:select的动态帮定
代码: <div data-role="navbar"> <ul> <li> <select name="select-choi ...
- Activiti5.16.4数据库表结构
一.ACTIVITI 数据库E-R图(5.16.4) Activiti 5.16.4 总共有24张表,增加act_evt_log(事件日志),以及增加了对SasS的支持. 在流程定义.运行实例和历史的 ...