放肆地使用UIBezierPath和CAShapeLayer画各种图形
2016-02-15 18:10:12 | 来源:玩转帮会 | 投稿:佚名 | 编辑:dations

原标题:放肆地使用UIBezierPath和CAShapeLayer画各种图形

CAShapeLayer 是 CALayer 的子类,但是比 CALayer 更灵活,可以画出各种图形,当然,你也可以使用其他方式来画,随你。

杂谈

在 CAShapeLayer 中,也可以像 CALayer 一样指定它的 frame 来画,就像这样:

letlayer=CAShapeLayer()
layer.frame=CGRectMake(110,100,150,100)
layer.backgroundColor=UIColor.blackColor().CGColor
view.layer.addSublayer(layer)

然后你就会得到如图这样的黑色矩形

但是,CAShapeLayer 有一个神奇的属性path用这个属性配合上 UIBezierPath 这个类就可以达到超神的效果。

UIBezierPath 顾名思义,这是用贝塞尔曲线的方式来构建一段弧线,你可以用任意条弧线来组成你想要的形状,比如,你想用它来和上面一样画一个矩形,那就可以这样子来做:

letpath=UIBezierPath(rect:CGRectMake(110,100,150,100))
letlayer=CAShapeLayer()
layer.path=path.CGPath
layer.fillColor=UIColor.blackColor().CGColor
view.layer.addSublayer(layer)

要注意的是,这里就不要用backgroundColor这个属性了,而要使用fillColor和strokeColor,前者代表设置这个 Layer 的填充色,后者代表设置它的边框色

layer.fillColor=UIColor.clearColor().CGCo
lorlayer.strokeColor=UIColor.blackColor().CGColor

可以试一下把上面代码设置颜色的部分改成这个样子,那么运行程序的时候就会是这种样子

玩一下UIBezierPath

在说回 UIBezierPath ,在 UIBezierPath 的 init 方法中,就有很多方便你画各种图形的方法,比如你可以画一个带圆角的图形

letpath=UIBezierPath(roundedRect:CGRectMake(110,100,150,100),cornerRadius:50)
letlayer=CAShapeLayer()
layer.path=path.CGPath
layer.fillColor=UIColor.clearColor().CGColor
layer.strokeColor=UIColor.blackColor().CGColor

还可以指定起始角和半径画圆

letradius:CGFloat=60.0
letstartAngle:CGFloat=0.0
letendAngle:CGFloat=CGFloat(M_PI*2)
letpath=UIBezierPath(arcCenter:view.center,radius:radius,startAngle:startAngle,endAngle:endAngle,clockwise:true)
letlayer=CAShapeLayer()
layer.path=path.CGPath
layer.fillColor=UIColor.clearColor().CGColor
layer.strokeColor=UIColor.blackColor().CGColor

在这里涉及到角度的问题,起始角和结束角,这里的角度使使用弧度制来表示,这里我收藏了一张图片,以方便参考。

怎么画曲线

贝塞尔曲线的画法是由起点、终点、控制点三个参数来画的,为了解释清楚这个点,我写了几行代码来解释它

letstartPoint=CGPointMake(50,300)
letendPoint=CGPointMake(300,300)
letcontrolPoint=CGPointMake(170,200)
letlayer1=CALayer()
layer1.frame=CGRectMake(startPoint.x,startPoint.y,5,5)
layer1.backgroundColor=UIColor.redColor().CGColor
letlayer2=CALayer()
layer2.frame=CGRectMake(endPoint.x,endPoint.y,5,5)
layer2.backgroundColor=UIColor.redColor().CGColor
letlayer3=CALayer()
layer3.frame=CGRectMake(controlPoint.x,controlPoint.y,5,5)
layer3.backgroundColor=UIColor.redColor().CGColor
letpath=UIBezierPath()
letlayer=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则曲线会慢慢变粗,这里的很多属性都是支持动画的。

示例代码

privatefuncanimation1(){
letanimation=CABasicAnimation(keyPath:"strokeEnd")
animation.fromValue=0
animation.toValue=1
animation.duration=2
layer.addAnimation(animation,forKey:"")
}
privatefuncanimation2(){
layer.strokeStart=0.5
layer.strokeEnd=0.5
letanimation=CABasicAnimation(keyPath:"strokeStart")
animation.fromValue=0.5
animation.toValue=0
animation.duration=2
letanimation2=CABasicAnimation(keyPath:"strokeEnd")
animation2.fromValue=0.5
animation2.toValue=1
animation2.duration=2
layer.addAnimation(animation,forKey:"")
layer.addAnimation(animation2,forKey:"")
}
privatefuncanimation3(){
letanimation=CABasicAnimation(keyPath:"lineWidth")
animation.fromValue=1
animation.toValue=10
animation.duration=2
layer.addAnimation(animation,forKey:"")
}

应用一下

前一阵子在仿时光网这个APP,其中有一个Layer的形状很怪异,是这样的

很明显它可以用 CAShapeLayer + UIBezierPath 来做,思路大概是这样,先移动到左上方的位置,然后向下划线,然后往右划线,然后往上划线,还剩一个盖子,这个盖子就用一个控制点控制曲率,非常简单,代码如下

letfinalSize=CGSizeMake(CGRectGetWidth(view.frame),400)
letlayerHeight=finalSize.height*0.2
letlayer=CAShapeLayer()
letbezier=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写的)

@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 这样的老套路来画,代码较多

-(CAShapeLayer*)eyeFirstLightLayer{
if(!_eyeFirstLightLayer){
_eyeFirstLightLayer=[CAShapeLayerlayer];
CGPointcenter=CGPointMake(CGRectGetWidth(self.frame)/2,CGRectGetHeight(self.frame)/2);
UIBezierPath*path=[UIBezierPathbezierPathWithArcCenter: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=[UIColorblackColor].CGColor;
_eyeFirstLightLayer.lineWidth=5.f;
_eyeFirstLightLayer.path=path.CGPath;
_eyeFirstLightLayer.fillColor=[UIColorclearColor].CGColor;
_eyeFirstLightLayer.strokeColor=[UIColorwhiteColor].CGColor;
}
return_eyeFirstLightLayer;
}
-(CAShapeLayer*)eyeSecondLightLayer{
if(!_eyeSecondLightLayer){
_eyeSecondLightLayer=[CAShapeLayerlayer];
CGPointcenter=CGPointMake(CGRectGetWidth(self.frame)/2,CGRectGetHeight(self.frame)/2);
UIBezierPath*path=[UIBezierPathbezierPathWithArcCenter: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=[UIColorblackColor].CGColor;
_eyeSecondLightLayer.lineWidth=5.f;
_eyeSecondLightLayer.path=path.CGPath;
_eyeSecondLightLayer.fillColor=[UIColorclearColor].CGColor;
_eyeSecondLightLayer.strokeColor=[UIColorwhiteColor].CGColor;
}
return_eyeSecondLightLayer;
}
-(CAShapeLayer*)eyeballLayer{
if(!_eyeballLayer){
_eyeballLayer=[CAShapeLayerlayer];
CGPointcenter=CGPointMake(CGRectGetWidth(self.frame)/2,CGRectGetHeight(self.frame)/2);
UIBezierPath*path=[UIBezierPathbezierPathWithArcCenter: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=[UIColorblackColor].CGColor;
_eyeballLayer.lineWidth=1.f;
_eyeballLayer.path=path.CGPath;
_eyeballLayer.fillColor=[UIColorclearColor].CGColor;
_eyeballLayer.strokeColor=[UIColorwhiteColor].CGColor;
_eyeballLayer.anchorPoint=CGPointMake(0.5,0.5);
}
return_eyeballLayer;
}
-(CAShapeLayer*)topEyesocketLayer{
if(!_topEyesocketLayer){
_topEyesocketLayer=[CAShapeLayerlayer];
CGPointcenter=CGPointMake(CGRectGetWidth(self.frame)/2,CGRectGetHeight(self.frame)/2);
UIBezierPath*path=[UIBezierPathbezierPath];
[pathmoveToPoint:CGPointMake(0,CGRectGetHeight(self.frame)/2)];
[pathaddQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame),CGRectGetHeight(self.frame)/2)
controlPoint:CGPointMake(CGRectGetWidth(self.frame)/2,center.y-center.y-20)];
_topEyesocketLayer.borderColor=[UIColorblackColor].CGColor;
_topEyesocketLayer.lineWidth=1.f;
_topEyesocketLayer.path=path.CGPath;
_topEyesocketLayer.fillColor=[UIColorclearColor].CGColor;
_topEyesocketLayer.strokeColor=[UIColorwhiteColor].CGColor;
}
return_topEyesocketLayer;
}
-(CAShapeLayer*)bottomEyesocketLayer{
if(!_bottomEyesocketLayer){
_bottomEyesocketLayer=[CAShapeLayerlayer];
CGPointcenter=CGPointMake(CGRectGetWidth(self.frame)/2,CGRectGetHeight(self.frame)/2);
UIBezierPath*path=[UIBezierPathbezierPath];
[pathmoveToPoint:CGPointMake(0,CGRectGetHeight(self.frame)/2)];
[pathaddQuadCurveToPoint:CGPointMake(CGRectGetWidth(self.frame),CGRectGetHeight(self.frame)/2)
controlPoint:CGPointMake(CGRectGetWidth(self.frame)/2,center.y+center.y+20)];
_bottomEyesocketLayer.borderColor=[UIColorblackColor].CGColor;
_bottomEyesocketLayer.lineWidth=1.f;
_bottomEyesocketLayer.path=path.CGPath;
_bottomEyesocketLayer.fillColor=[UIColorclearColor].CGColor;
_bottomEyesocketLayer.strokeColor=[UIColorwhiteColor].CGColor;
}
return_bottomEyesocketLayer;
}

然后更改一下某些属性的值,方便稍后的动画

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

tags:

上一篇  下一篇

相关:

qq同步助手怎么把备份的通讯录和短信导出电脑qq同步助手通讯录和短信导出电脑方法

  qq同步助手怎么把备份的通讯录和短信导出电脑?qq同步助手通讯录和短信导出电脑方法。大家使用qq同步助

Java中的中文编码问题深入分析

编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切

实体游戏卖不动,最大的游戏零售商要转型做发行了

当实体书店面和唱片店面临着电子书与音乐流媒体的竞争时,游戏界也好不到哪里去。世界上最大的电子游戏硬件

《刺客信条:身份》,大概是最接近原作的手游

不知道有多少人记得在 2014 年 9 月的时候,育碧曾经推出过一款名为《刺客信条:身份》的手机游戏。当时,

乐高版星战游戏告诉你,原力觉醒之前都发生了什么

刚刚看完《星球大战:原力觉醒》还没多久,这部电影的游戏就要来了。乐高集团联合华纳兄弟互动娱乐、Take T

暴雪讨好新玩家,就给《炉石传说》来了个大改动

《炉石传说》,这款全球超过 3000 万玩家热爱的游戏将迎来诞生后最大的一次更新。今天凌晨,暴雪通过战网公

英超更换联赛标志,背后可能是品牌策略的改变

头戴王冠的蓝色雄狮,单爪稳住红色足球,若是足球迷或英国文化爱好者一定不会对这个图案感到陌生。自 1992

苹果iPhone与Apple Watch遭控侵犯触控专利

苹果的3D Touch触控技术。 图片来源: Apple 专门研发触控反馈技术的Immersion上周向美国地方法院及美国国

前两天刷屏的引力波,未来可能有这些发展前景

我先简单说几句立即能想到的。短期内, 摆在 LIGO 和其他类似激光干涉引力波实验项目面前的大概有这么几个事

吃到花椒时,记得体会一下「50赫兹的震颤感」

花椒所带来「麻」的感觉,是一种触觉,准确地说等同于 50 赫兹的震颤。精准地来说,是由花椒所含有的 hydro

站长推荐: