转载;//www.greatytc.com/p/bb934ca504d1
1. CAShapeLayer
CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类,使用CAShapeLayer有以下一些优点:
渲染快速——CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多
高效使用内存——一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存
不会被图层边界剪裁掉——一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉
不会出现像素化——当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化
CAShapeLayer可以用来绘制所有能够通过CGPath来表示的形状。这个形状不一定要闭合,图层路径也不一定要不可破,事实上你可以在一个图层上绘制好几个不同的形状。你可以控制一些属性比如lineWith,lineCap,和lineJoin。但是在图层层面你只有一次机会设置这些属性,如果你想用不同颜色或风格来绘制多个形状,就不得不为每个形状准备一个图层了。
说了这么多,CAShapeLayer到底能用来做什么呢?
CAShapeLayer实现视图的部分圆角:
-(void)drawCorner{UIView*view = [[UIViewalloc] initWithFrame:CGRectMake(0.0f,0.0f,100.0f,100.0f)]; view.center =self.view.center; view.backgroundColor = [UIColorblackColor]; [self.view addSubview:view];UIBezierPath*path = [UIBezierPathbezierPathWithRoundedRect:view.frame byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomRightcornerRadii:CGSizeMake(30.0f,30.0f)];CAShapeLayer*layer = [[CAShapeLayeralloc] init]; layer.bounds = view.frame; layer.position =CGPointMake(50.0f,50.0f); layer.path = path.CGPath; view.layer.mask = layer;}
运行效果:
Paste_Image.png
CAShapeLayer实现一个呆萌的火柴人:
-(void)drawMatchman{CGFloatradius =25.0f;//半径UIBezierPath*path = [[UIBezierPathalloc] init];CGPointpoint1 =CGPointMake(self.view.center.x + radius,self.view.center.y); [path moveToPoint:point1];//将画笔移动到point1[path addArcWithCenter:self.view.center radius:radius startAngle:0.0f endAngle:2.0f*M_PI clockwise:YES];//画一个圆代表火柴人的头CGPointpoint2 =CGPointMake(point1.x - radius, point1.y + radius); [path moveToPoint:point2];//将画笔移动到point2,准备画身体CGPointpoint3 =CGPointMake(point2.x, point2.y+50.0f); [path addLineToPoint:point3];//画一根长为50的竖线代表火柴人的身体,起点是point2,终点是point3CGPointpoint4 =CGPointMake(point3.x - radius, point3.y+25.0f); [path addLineToPoint:point4];//画一根长为25的左斜线代表火柴人的左脚,起点是point3,终点是point4[path moveToPoint:point3];//将画笔移动到point3,准备画右脚CGPointpoint5 =CGPointMake(point3.x + radius, point3.y+25.0f); [path addLineToPoint:point5];//画一根长为25的右斜线代表火柴人的右脚,起点是point3,终点是point5//最后画一根横线,代表火柴人的手CGPointpoint6 =CGPointMake(point2.x - radius, point2.y +25.0f); [path moveToPoint:point6]; [path addLineToPoint:CGPointMake(point6.x +50.0f, point6.y)]; shapeLayer = [CAShapeLayerlayer]; shapeLayer.strokeColor = [UIColorredColor].CGColor;//画笔颜色shapeLayer.fillColor = [UIColorclearColor].CGColor;//填充色shapeLayer.lineWidth =6.0f;//线条宽度shapeLayer.lineJoin = kCALineJoinRound;//线条连接处的样式shapeLayer.lineCap = kCALineCapRound;//线条末端处的样式shapeLayer.path = path.CGPath; [self.view.layer addSublayer:shapeLayer]; [selfaddFlagPoint:point1]; [selfaddFlagPoint:point2]; [selfaddFlagPoint:point3]; [selfaddFlagPoint:point4]; [selfaddFlagPoint:point5]; [selfaddFlagPoint:point6];}-(void)addFlagPoint:(CGPoint)aPoint{UIView*flag = [[UIViewalloc] initWithFrame:CGRectMake(0.0f,0.0f,6.0f,6.0f)]; flag.center = aPoint; flag.layer.cornerRadius =3.0f; flag.backgroundColor = [UIColorblackColor]; [self.view addSubview:flag];}
运行效果:
Paste_Image.png
CAShapeLayer实现一个扇形动画:
-(void)drawCircular{ UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0f,0.0f,100.0f,100.0f)];view.center = self.view.center;UIImage *image = [UIImage imageNamed:@"3"];view.layer.contents = (__bridge id _Nullable)(image.CGImage);view.layer.contentsGravity = kCAGravityCenter;view.layer.contentsScale = [UIScreen mainScreen].scale;[self.viewaddSubview:view];shapeLayer= [CAShapeLayer layer];shapeLayer.frame= view.bounds;shapeLayer.strokeEnd=0.0f;shapeLayer.strokeStart=0.0f;UIBezierPath *path = [UIBezierPathbezierPathWithOvalInRect:view.bounds];shapeLayer.path= path.CGPath;shapeLayer.fillColor= [UIColor clearColor].CGColor;shapeLayer.lineWidth=100.0f;shapeLayer.strokeColor= [UIColor redColor].CGColor;view.layer.mask =shapeLayer;[NSTimerscheduledTimerWithTimeInterval:0.005ftarget:self selector:@selector(timerAction) userInfo:nil repeats:YES];}-(void)timerAction{ staticBOOLflag = NO;if (shapeLayer.strokeEnd>=1.5f) { flag = YES;} if (shapeLayer.strokeEnd<= -0.5f) { flag = NO;} if (flag) {shapeLayer.strokeEnd-=0.005f;}else{shapeLayer.strokeEnd+=0.005f;}}
运行效果:
running.gif
更多关于CAShapeLayer的动画:动画黄金搭档:CADisplayLink & CAShapeLayer
2. CATransformLayer
之前我们在CoreAnimation之变换中构造了一个残缺的正方体,最后在旋转正方体的时候遇到了问题,原因在于CALayer是扁平的,所以直接将superLayer绕y轴旋转的时候看不出正方体的3D效果
CoreAnimation有一个专用图层叫CATransformLayer,它是CALayer的子类,但是不同于普通的CALayer,因为它不能显示它自己的内容,只有当存在了一个能作用域子图层的变换它才真正存在,而且CATransformLayer并不平面化它的子图层,所以它能够用于构造一个层级的3D结构
总之一句话,CATransformLayer相当于一个容器,一个3D的容器
这次我们依然以构建一个正方体为例,开始写代码前,我们不妨在脑袋里先构造一下这个正方体:
首先,有六块木板,都是平放在一个3D空间里
第一步,构建上下两面,把上面这块木板往上移动50个点(即沿z轴移动50个点);再把下面这块木板往下移动50个点(即沿z轴移动-50个点),这样就构建出了上下两面
第二步,构建左右两面,将左面这块木板往左边直立起来(即沿y轴旋转-90度),再将左面这块木板往上移动50个点(即沿z轴移动50个点);同理,右面这块木板就是先往右边直立起来再往上移动
第三步,构建前后两面,将前面这块木板往前边直立起来(即沿x轴旋转-90度),再将前面这块木板往上移动50个点(即沿z轴移动50个点);同理,后面这块木板就是先往后边直立起来再往上移动
代码如下(楼主也是初学,为了逻辑清晰,就没有封装方法,直接一个面一个方法,所以代码有点多哈):
#import"ViewController.h"@interfaceViewController()@end@implementationViewController{CATransformLayer*transformLayer;NSTimer*timer;}- (void)viewDidLoad { [superviewDidLoad];// Do any additional setup after loading the view, typically from a nib.CATransform3DsublayerTransform =CATransform3DIdentity; sublayerTransform.m34 =-1.0f/500.0f;self.view.layer.sublayerTransform = sublayerTransform; transformLayer = [CATransformLayerlayer]; transformLayer.position =self.view.layer.position;CATransform3Dtransform =CATransform3DIdentity; transform =CATransform3DRotate(transform, -M_PI_4,1.0f,0.0f,0.0f); transform =CATransform3DRotate(transform, -M_PI_4,0.0f,1.0f,0.0f); transformLayer.transform = transform; [self.view.layer addSublayer:transformLayer]; [selfaddFace1]; [selfaddFace2]; [selfaddFace3]; [selfaddFace4]; [selfaddFace5]; [selfaddFace6];}-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{ [timer invalidate]; timer =nil; timer = [NSTimerscheduledTimerWithTimeInterval:0.025f target:selfselector:@selector(timerAction) userInfo:nilrepeats:YES];}-(void)timerAction{CATransform3Dtransform = transformLayer.transform; transform =CATransform3DRotate(transform,1.0f/180.0f*M_PI,1.0f,1.0f,0.0f); transformLayer.transform = transform;}//上面 ———— 把上面这块木板往上移动50个点(即沿z轴移动50个点)-(void)addFace1{CALayer*layer = [[CALayeralloc] init]; layer.bounds =CGRectMake(0.0f,0.0f,100.0f,100.0f); layer.backgroundColor = [UIColorredColor].CGColor;CATransform3Dtransform =CATransform3DIdentity; transform =CATransform3DTranslate(transform,0.0f,0.0f,50.0f); layer.transform = transform; [transformLayer addSublayer:layer];}//下面 ———— 把下面这块木板往下移动50个点(即沿z轴移动-50个点)-(void)addFace2{CALayer*layer = [[CALayeralloc] init]; layer.bounds =CGRectMake(0.0f,0.0f,100.0f,100.0f); layer.backgroundColor = [UIColororangeColor].CGColor;CATransform3Dtransform =CATransform3DIdentity; transform =CATransform3DTranslate(transform,0.0f,0.0f,-50.0f); layer.transform = transform; [transformLayer addSublayer:layer];}//左面-(void)addFace3{CALayer*layer = [[CALayeralloc] init]; layer.bounds =CGRectMake(0.0f,0.0f,100.0f,100.0f); layer.backgroundColor = [UIColoryellowColor].CGColor;CATransform3Dtransform =CATransform3DIdentity;//将左面这块木板往左边直立起来(即沿y轴旋转-90度)transform =CATransform3DRotate(transform, -M_PI_2,0.0f,1.0f,0.0f);//再将左面这块木板往上移动50个点(即沿z轴移动50个点)transform =CATransform3DTranslate(transform,0.0f,0.0f,50.0f); layer.transform = transform; [transformLayer addSublayer:layer];}//右面-(void)addFace4{CALayer*layer = [[CALayeralloc] init]; layer.bounds =CGRectMake(0.0f,0.0f,100.0f,100.0f); layer.backgroundColor = [UIColorgreenColor].CGColor;CATransform3Dtransform =CATransform3DIdentity;//将右面这块木板往右边直立起来(即沿y轴旋转90度)transform =CATransform3DRotate(transform, M_PI_2,0.0f,1.0f,0.0f);//再将右面这块木板往上移动50个点(即沿z轴移动50个点)transform =CATransform3DTranslate(transform,0.0f,0.0f,50.0f); layer.transform = transform; [transformLayer addSublayer:layer];}//前面-(void)addFace5{CALayer*layer = [[CALayeralloc] init]; layer.bounds =CGRectMake(0.0f,0.0f,100.0f,100.0f); layer.backgroundColor = [UIColorblueColor].CGColor;CATransform3Dtransform =CATransform3DIdentity;//将前面这块木板往前边直立起来(即沿x轴旋转-90度)transform =CATransform3DRotate(transform, -M_PI_2,1.0f,0.0f,0.0f);//再将前面这块木板往上移动50个点(即沿z轴移动50个点)transform =CATransform3DTranslate(transform,0.0f,0.0f,50.0f); layer.transform = transform; [transformLayer addSublayer:layer];}//后面-(void)addFace6{CALayer*layer = [[CALayeralloc] init]; layer.bounds =CGRectMake(0.0f,0.0f,100.0f,100.0f); layer.backgroundColor = [UIColorpurpleColor].CGColor;CATransform3Dtransform =CATransform3DIdentity;//将后面这块木板往后边直立起来(即沿x轴旋转90度)transform =CATransform3DRotate(transform, M_PI_2,1.0f,0.0f,0.0f);//再将后面这块木板往上移动50个点(即沿z轴移动50个点)transform =CATransform3DTranslate(transform,0.0f,0.0f,50.0f); layer.transform = transform; [transformLayer addSublayer:layer];}@end
以前在CALayer上旋转正方体的时候我们要这样写(即把所有的子图层挨个儿做一次变换):
-(void)timerAction{staticCGFloatangle =1.0f;CATransform3Dtransform3d =self.containerView.layer.sublayerTransform; transform3d =CATransform3DRotate(transform3d, angle/180.0f*M_PI,0.0f,1.0f,0.0f);self.containerView.layer.sublayerTransform = transform3d;}
而现在在CATransformLayer上旋转正方体,只需要将CATransformLayer绕x轴或者绕y轴旋转就行了:
-(void)timerAction{ CATransform3Dtransform= transformLayer.transform;transform= CATransform3DRotate(transform,1.0f/180.0f*M_PI,1.0f,1.0f,0.0f); transformLayer.transform=transform;}
运行效果:
running.gif
3. CAGradientLayer
CAGradientLayer可以用来实现渐变效果:
CAGradientLayer*layer = [CAGradientLayerlayer]; layer.bounds =CGRectMake(0.0f,0.0f,150.0f,150.0f); layer.position =self.view.layer.position; layer.colors = @[(__bridgeid)[UIColorredColor].CGColor,(__bridgeid)[UIColorgreenColor].CGColor,(__bridgeid)[UIColorblueColor].CGColor]; layer.locations = @[@.25,@0.5,@0.75]; layer.startPoint =CGPointMake(0.0f,0.0f); layer.endPoint =CGPointMake(1.0f,0.0f); [self.view.layer addSublayer:layer];
运行效果:
Paste_Image.png
需要特别说明一下locations这个属性,locations数组里面装的是相对位置,这个相对位置必须是单调递增的,但是这个位置并不是代表颜色的位置,而是说从这个位置开始,将要开始渐变成下一个颜色了
拿示例代码来说,从0.25开始,将要由红变绿了,从0.5开始将要由绿变蓝了,从0.75开始又要开始下一个渐变了,但是由于没有下一个颜色了,所以后面全是蓝色,你可以在示例代码的colors里面再添加一个颜色试试
4. CAReplicatorLayer
学习CAReplicatorLayer之前,我们再来复习一下变换的顺序:
#import"ViewController.h"@interfaceViewController()@end@implementationViewController{CALayer*layer;}- (void)viewDidLoad { [superviewDidLoad]; layer = [CALayerlayer]; layer.backgroundColor = [UIColorcyanColor].CGColor;UIImage*image = [UIImageimageNamed:@"3"]; layer.frame =CGRectMake(110.0f,100.0f,100.0f,100.0f); layer.contents = (__bridgeid)image.CGImage; layer.contentsGravity = kCAGravityResizeAspect; layer.contentsScale = [UIScreenmainScreen].scale; [self.view.layer addSublayer:layer];}-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{ [selfrotate];}-(void)rotate{ __weak__typeof__(self) weakSelf =self; [UIViewanimateWithDuration:3.0f animations:^{CATransform3Dtransform = layer.transform; transform =CATransform3DRotate(transform, M_PI,0.0f,0.0f,1.0f); layer.transform = transform; } completion:^(BOOLfinished) { [weakSelf translate]; }];}-(void)translate{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [UIViewanimateWithDuration:3.0f animations:^{CATransform3Dtransform = layer.transform; transform =CATransform3DTranslate(transform,0.0f,100.0f,0.0f); layer.transform = transform; }]; });}@end
运行效果
running.gif
注意这句代码:
transform = CATransform3DTranslate(transform,0.0f,100.0f,0.0f);
明明是沿y轴移动200个点,为啥运行的时候会往上跑呢❓
原因在于,在移动之前,我们已经绕z轴旋转过了:
transform = CATransform3DRotate(transform, M_PI,0.0f,0.0f,1.0f);
旋转之后的layer,其相对于superLayer的坐标系已经发生了改变
拿示例代码来说,绕z轴旋转180度之后,x轴和y轴的方向都已经变成与原来相反的方向了
变换的顺序,在CAReplicatorLayer中体现得尤为明显,因为CAReplicatorLayer的instance的变换是逐步增加的,每个实例都是相对于前一实例布局
CAReplicatorLayer实现一个带倒影的ImageView:
#import"ReplicatorImageView.h"@implementationReplicatorImageView+ (Class)layerClass{return[CAReplicatorLayerclass];}- (void)setUp{CAReplicatorLayer*layer = (CAReplicatorLayer*)self.layer; layer.instanceCount =2;CATransform3Dtransform =CATransform3DIdentity; transform =CATransform3DScale(transform,1,-1,0); transform =CATransform3DTranslate(transform,0.0f, -self.frame.size.height,0.0f); layer.instanceTransform = transform; layer.instanceAlphaOffset =-0.6;CALayer*imageLayer = [CALayerlayer]; imageLayer.frame = layer.bounds; imageLayer.contents = (__bridgeid_Nullable)(self.image.CGImage); imageLayer.contentsScale = [UIScreenmainScreen].scale; imageLayer.contentsGravity = kCAGravityResizeAspect; [layer addSublayer:imageLayer];}-(void)setReplicatorImage:(UIImage*)replicatorImage{self.image = replicatorImage; [selfsetUp];}@end
- (void)viewDidLoad { [superviewDidLoad]; ReplicatorImageView *imageView = [[ReplicatorImageView alloc] initWithFrame:CGRectMake(0.0f,0.0f,150.0f,150.0f)]; imageView.center =self.view.center; [self.view addSubview:imageView];UIImage*image = [UIImageimageNamed:@"3"]; imageView.replicatorImage = image;}
运行效果:
Paste_Image.png
instanceCount指定了总共要复制多少个图层(包含本身)
instanceAlphaOffset = -0.6f; 即当前图层实例的alpha值 = 上一个图层实例的alpha - 0.6f,与之类似的属性还有这些:
/* The color components added to the color ofinstancek-1 to produce * the modulation color ofinstancek. Defaults to the clear color (no * change). Animatable. */@propertyfloatinstanceRedOffset;@propertyfloatinstanceGreenOffset;@propertyfloatinstanceBlueOffset;
更多CAReplicatorLayer动画:基于CAReplicatorLayer的炫酷动画