放肆地使用UIBezierPath和CAShapeLayer画各种图形

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 可以画出你想要的任何形状,没有它做不到,只有你想不到,搞定了它们你就可以轻松定制你想要的任何控件了。

时间: 2024-12-03 04:37:37

放肆地使用UIBezierPath和CAShapeLayer画各种图形的相关文章

ppt怎么画不规则图形?

  ppt怎么画不规则图形?制作PPT的时候常常需要自己绘制不规则的图形达到表达意念的目的,默认的模块不易编辑,需要字型绘制. 1.打开PPT,新建一个页面. 2.检查默认形状中是否有自己要用的形状,或者可以通过简单编辑达到目的的形状. 3.如果没有,选择"任意多边形"的工具,自己进行绘制. 4.比如需要绘制一个图中所示的图形.选择"任意多边形"工具根据图中形状进行描绘. 5.图形绘制完成. 6.将参考图删除,可以根据自己的需要进行透明度.边框的调整.

怎么选中你画的图形并能够拖动它?

问题描述 就是当你点击你画的图形时,表示选中它,按住不放的话又可以拖动它!!!给点源代码吧大侠们!!! 解决方案 解决方案二:参考:[img=http://p.blog.csdn.net/images/p_blog_csdn_net/zswang/%E6%89%AE%E9%85%B7.gif]图[/img]解决方案三:我大概提一个思路给楼主参考吧,把图型放到一个Form里,把Form的标题栏去掉,设置Form可以任意拖动解决方案四:简单的做法是每个图形都从Control继承过来,实现Paint.

opencv在视频里画不规则图形

问题描述 opencv在视频里画不规则图形 在图片里可以,在视频里就不行了,代码如下: /****************************************** * 使用说明: * * 左键单击画点 * * 右键自动闭合曲线 * * 右键双击上色 * ******************************************/ #include <string> #include <list> #include <vector> #include

java-我想用swing画一个图形,但是不知道怎么将坐标原点设在panel的中心

问题描述 我想用swing画一个图形,但是不知道怎么将坐标原点设在panel的中心 我想画一个图,当坐标原点不在左上角,而是在整个panel的中心的时候最好画了,我应该用什么方法才能设置? 解决方案 可以先获取panel的长度和高度,新建一个点对象设置X,Y属性为panel的长与高的一半 解决方案二: drawline不是可以设置起点的吗?

android-安卓 OpenGL ES画3D图形问题,选中的板件红色标记,会透明显示在最前面

问题描述 安卓 OpenGL ES画3D图形问题,选中的板件红色标记,会透明显示在最前面 问题如图片和标题. 因为选中的板件,想让看到内部的螺丝接口,那必须关闭深度测试,可是关闭了就会这样,显示在最前方.打开了就无法显示螺丝接口-- 解决方案 改变对应的两个部件的透明度

如何在获取方向传感器数据后画一个图形

问题描述 如何在获取方向传感器数据后画一个图形 我用onsensorchange来获取方向传感器的三个数据,想用ondraw画一个矩形, 但是这两个部分在不同的activity里,我想用方向传感器获取的数据在另一个activity里使用,画图那个activity是继承view类的 我还是个小白,谢过各位大神 解决方案 使用startActivityForResult方法获取你存储的参数信息.希望能帮到你 解决方案二: 我想知道如何获取三维加速度传感器的数据???都跟哪些知识有关??

mfc-图像处理之MFC如何读取在客户区画的图形数据

问题描述 图像处理之MFC如何读取在客户区画的图形数据 看了杨淑莹的图像模式识别-VC++技术实现,发现她的程序是在画图的时候 用一个10*10矩形框作为画笔的大小,然后用for把数据循环存到*(m_pData+LineBytes*(height-y)+x)=0:里面的 问题:那能不能先用CPen来画图,然后读取像素呢,或者有没有更好的 思路,因为我觉得这样存储有点费劲,谢谢大神们!^_^ 解决方案 能不能先用CPen来画图?答案肯定是能,关键是你绘制在哪里?还是要一块内存区域吧,如果是为什么不

java 按钮监听器-java 点一下按钮就能画一个图形,怎么弄呢

问题描述 java 点一下按钮就能画一个图形,怎么弄呢 import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener; import javax.swing.*;public class huatu { static public void main(String[] args) { new test(); }}class test extends JFrame { JButton

C#程序画的图形怎样按一定角度旋转

问题描述 C#画一个图形该图形所有坐标都是相对于一个矩形,怎样旋转矩形使图形也旋转 解决方案 解决方案二:graphic有translate系列方法,可以旋转画布,不用改变你的坐标值