Core Animation图层不仅仅能作用于图片和颜色而已,它提供了很多专项功能的图层,更方便我们直接使用。
文章仅是简要概述了一些专用图层以及用他们实现的一些效果,后续会另作博客深入研究。
为了获得Core Animation最好的性能,你需要为你的工作选对正确的工具,希望你能够挖掘这些不同的CALayer
子类的功能。
CAShapeLayer
继承自CALayer,常配合与UIBezierPath使用,图层除了继承父类之外的大多属性之外同UIBezierPath的很多属性类似。
同用Core Graphics直接向原始的CALyer
的内容中绘制一个路径,相比之下,使用CAShapeLayer
有以下一些优点:
- 渲染快速。
CAShapeLayer
使用了硬件加速,绘制同一图形会比用Core Graphics快很多。 - 高效使用内存。一个
CAShapeLayer
不需要像普通CALayer
一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。 - 不会被图层边界剪裁掉。一个
CAShapeLayer
可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer
一样被剪裁掉。 - 不会出现像素化。当你给
CAShapeLayer
做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
栗子:
One:绘制一个椭圆。
// 创建一个路径对象
UIBezierPath *linePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 200, 50)];
// 设置路径画布
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.bounds = (CGRect){0,0,200,200};
lineLayer.position = CGPointMake(100, 100);
lineLayer.lineWidth = 2.0;
lineLayer.strokeColor = [UIColor blueColor].CGColor; // 边线颜色
lineLayer.path = linePath.CGPath;
lineLayer.fillColor = nil; // 默认是black
// 添加到图层上
[self.layer addSublayer:lineLayer];
效果:
B91B8722-75D3-4F97-BC97-F05BE1CF12C6.pngCATextLayer
Core Animation提供了一个
CALayer
的子类CATextLayer
,它以图层的形式包含了UILabel
几乎所有的绘制特性,并且额外提供了一些新的特性。
CATextLayer
使用了Core text,要比UILabel
渲染得快得多
UILabel
使用的是webKit
绘制的。
属性:
-
string
:文本呈现,可以是一个NSString或者NSAttributedString;默认为nil。
-
foregroundColor
:字体颜色。 -
alignmentMode
:描述如何在该层中的单行的文本对齐,设置字体的排列格式,可选择自然,左,右,居中和自适应;默认为自然。
kCAAlignmentNatural:
kCAAlignmentLeft
kCAAlignmentRight
kCAAlignmentCenter
kCAAlignmentJustified
-
contentsScale
:使用CATextLayer设置文本,可能会产生模糊状态,因为该默认的分辨率不是retina,设置如下代码即可:
<注>:把CATextLayer作为宿主图层的另一好处就是视图自动设置了contentsScale属性。
textLayer.contentsScale = [UIScreen mainScreen].scale;
-
truncationMode
:描述如何将字符串截断以适应图层大小,设置缩短的部位
kCATruncationNone
kCATruncationStart
kCATruncationEnd
kCATruncationMiddle
-
wrapped
:文本是否自动适应图层大小,default:NO。
-
fontSize
:文本字体大小,默认为36;仅当string不是一个NSAttributedString的时候使用。 -
font
:字体使用,可能是一个CTFontRef,一个CGFontRef或者一个字符串命名体,默认为Helvetica字体;仅当string不是一个NSAttributedString的时候使用。
赋值:
UIFont *font = [UIFont systemFontOfSize:13];
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
textLayer.font = fontRef;
栗子:
我们通过自定义一个
UILabel
的子类,在实现中重写+(Class)layerClass
方法来返回子类的宿主图层。通过重写子类有关UILabel
的set
方法来完成对宿主图层的一些属性设置。
+(Class)layerClass
{
return [CATextLayer class];
}
- (CATextLayer *)textLayer
{
return (CATextLayer *)self.layer;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setupSubview];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self setupSubview];
}
return self;
}
- (void)setupSubview{
self.text = self.text;
self.textColor = self.textColor;
self.font = self.font;
self.layer.contentsScale = [UIScreen mainScreen].scale;
[self textLayer].alignmentMode = kCAAlignmentJustified;
[self textLayer].wrapped = YES;
[self.layer display];
}
- (void)setText:(NSString *)text
{
super.text = text;
[self textLayer].string = text;
}
- (void)setTextColor:(UIColor *)textColor
{
super.textColor = textColor;
[self textLayer].foregroundColor = textColor.CGColor;
}
- (void)setFont:(UIFont *)font
{
super.font = font;
//set layer font
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
[self textLayer].font = fontRef;
[self textLayer].fontSize = font.pointSize;
CGFontRelease(fontRef);
}
类的使用同UILabel的方法使用一样。通过添加控件效果如下:
CATextLayerCATransformLayer
CATransformLayer
不同于普通的CALayer
,因为它不能显示它自己的内容。只有当存在了一个能作用于子图层的变换它才真正存在。CATransformLayer
并不平面化它的子图层,所以它能够用于构造一个层级的3D结构可以理解为装不同子图层的容器(个人理解)。
栗子:(画两个正方体)
//两个立方体:
- (void)transformAction{
UIView * containView = [[UIView alloc]initWithFrame:self.view.bounds];
containView.center = self.view.center;
containView.backgroundColor = self.view.backgroundColor;
self.containView = containView;
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1.0 / 500.0;
containView.layer.sublayerTransform = perspective;
[self.view addSubview:self.containView];
//set up the transform for cube 1 and add it
CATransform3D c1t = CATransform3DIdentity;
c1t = CATransform3DTranslate(c1t, -100, 0, 0);
CALayer *cube1 = [self cubeWithTransform:c1t];
[self.containView.layer addSublayer:cube1];
//set up the transform for cube 2 and add it
CATransform3D c2t = CATransform3DIdentity;
c2t = CATransform3DTranslate(c2t, 100, 0, 0);
c2t = CATransform3DRotate(c2t, -M_PI_4, 1, 0, 0);
c2t = CATransform3DRotate(c2t, -M_PI_4, 0, 1, 0);
CALayer *cube2 = [self cubeWithTransform:c2t];
[self.containView.layer addSublayer:cube2];
}
为每个立方体添加六个面:
- (CALayer *)cubeWithTransform:(CATransform3D)transform
{
//create cube layer
CATransformLayer *cube = [CATransformLayer layer];
//add cube face 1
CATransform3D ct = CATransform3DMakeTranslation(0, 0, 50);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 2
ct = CATransform3DMakeTranslation(50, 0, 0);
ct = CATransform3DRotate(ct, M_PI_2, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 3
ct = CATransform3DMakeTranslation(0, -50, 0);
ct = CATransform3DRotate(ct, M_PI_2, 1, 0, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 4
ct = CATransform3DMakeTranslation(0, 50, 0);
ct = CATransform3DRotate(ct, -M_PI_2, 1, 0, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 5
ct = CATransform3DMakeTranslation(-50, 0, 0);
ct = CATransform3DRotate(ct, -M_PI_2, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//add cube face 6
ct = CATransform3DMakeTranslation(0, 0, -50);
ct = CATransform3DRotate(ct, M_PI, 0, 1, 0);
[cube addSublayer:[self faceWithTransform:ct]];
//center the cube layer within the container
CGSize containerSize = self.containView.bounds.size;
cube.position = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
//apply the transform and return
cube.transform = transform;
return cube;
}
为每个面设置颜色及旋转:
- (CALayer *)faceWithTransform:(CATransform3D)transform
{
//create cube face layer
CALayer *face = [CALayer layer];
face.frame = CGRectMake(-50, -50, 100, 100);
face.backgroundColor = NTRandomColor.CGColor;
face.transform = transform;
return face;
}
效果:
8F4A3245-8FA8-4022-9025-A85242EC99CC.png.jpegCAGradientLayer
用来生成两种或更多颜色平滑渐变的,采用硬件加速。
参考链接:http://www.jianshu.com/p/1c8ef3116b42
属性:
-
startPoint
,endPoint
:决定了渐变的方向,采用单位坐标系。 -
colors
:放置渐变色彩的数组,CGColorRef类型。
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id) [UIColor yellowColor].CGColor, (__bridge id)[UIColor greenColor].CGColor];
-
locations
:存有浮点数值的数组,每个index元素对应colors
中的index元素的颜色。两个数组的大小相同,否则将会得到一个空白的渐变。
gradientLayer.locations = @[@0.0, @0.25, @0.5];
CAReplicatorLayer
参考链接:
swift实现动画,部分属性介绍
四个CAReplicatorLayer小动画
桃心路径的动画
DEMO下载
https://github.com/YD-young/CAReplicatorLayer-DEMO
CAReplicatorLayer
的目的是为了高效生成许多相似的图层。它会绘制一个或多个图层的子图层,并在每个复制体上应用不同的变换。变换是逐步增加的,每个实例都是相对于前一实例布局。
CAReplicatorLayer
真正应用到实际程序上的场景比如:一个游戏中导弹的轨迹云,或者粒子爆炸(尽管iOS 5已经引入了CAEmitterLayer
,它更适合创建任意的粒子效果)
属性:
-
instanceCount
:指定了图层需要重复多少次。 -
instanceTransform
:指定了一个CATransform3D
3D变换(这种情况下,下一图层的位移和旋转将会移动到圆圈的下一个点)。 -
instanceBlueOffset
、instanceGreenOffset
:实现颜色的偏移变化。
栗子:(拷贝多个图层按照一定的弧度排列显示)
UIView * containView = [[UIView alloc]initWithFrame:self.view.bounds];
containView.center = self.view.center;
containView.backgroundColor = [UIColor yellowColor];
self.containView = containView;
[self.view addSubview:self.containView];
CAReplicatorLayer * replicatorLayer = [CAReplicatorLayer layer];
replicatorLayer.instanceCount = 5;
replicatorLayer.instanceBlueOffset =0.2;
replicatorLayer.instanceRedOffset = 0.3;
[self.containView.layer addSublayer:replicatorLayer];
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DTranslate(transform, 0, 200, 0);
transform = CATransform3DRotate(transform, M_PI / 5.0, 0, 0, 1);
transform = CATransform3DTranslate(transform, 0, -200, 0);
replicatorLayer.instanceTransform = transform;
CALayer * layer = [CALayer layer];
layer.backgroundColor = [UIColor greenColor].CGColor;
layer.frame= CGRectMake(10, 100, 50, 50);
[replicatorLayer addSublayer:layer];
效果:
D0139924-3CCA-4ABA-B4A1-3D78E02FF874.png.jpegCAScrollLayer
从layer层实现类似于tableView的功能,但并不负责将触摸事件转换为滑动事件,既不渲染滚动条,也不实现任何iOS指定行为例如滑动反弹。
- -scrollToPoint:
:自动适应
bounds的原点以便图层内容出现在滑动的地方。方法从图层树中查找并找到第一个可用的
CAScrollLayer`,然后滑动它使得指定点成为可视的。 -
scrollRectToVisible
:方法实现了同样的事情只不过是作用在一个矩形上的。 -
visibleRect
:属性决定图层(如果存在的话)的哪部分是当前的可视区域。
CAScrollLayer
有一个潜在的有用特性。如果你查看CAScrollLayer
的头文件,你就会注意到有一个扩展分类实现了一些方法和属性:
- (void)scrollPoint:(CGPoint)p;
- (void)scrollRectToVisible:(CGRect)r;
@property(readonly) CGRect visibleRect;
CATiledLayer
CATiledLayer`为载入大图造成的性能问题提供了一个解决方案:将大图分解成小片然后将他们单独按需载入
-
tileSize
: 是以像素为单位,更改CATiledLayer
的默认小图大小(256×256,retina情况下默认是128×128)。
需要做的是适应小图渲染代码以对应安排scale
的变化:
CGRect bounds = CGContextGetClipBoundingBox(ctx);
CGFloat scale = [UIScreen mainScreen].scale;
NSInteger x = floor(bounds.origin.x / layer.tileSize.width * scale);
NSInteger y = floor(bounds.origin.y / layer.tileSize.height * scale);
当需要载入新的小图时,CATiledLayer
就会调用到这个方法:-drawLayer:inContext
。(需设置代理),-drawLayer:inContext:
方法可以在多个线程中同时地并发调用。
CAEmitterLayer
在iOS 5中,苹果引入了一个新的
CALayer
子类叫做CAEmitterLayer
。CAEmitterLayer
是一个高性能的粒子引擎,被用来创建实时例子动画如:烟雾,火,雨等等这些效果。
CAEmitterLayer
看上去像是许多CAEmitterCell
的容器,这些CAEmitierCell
定义了一个例子效果。你将会为不同的例子效果定义一个或多个CAEmitterCell
作为模版,同时CAEmitterLayer
负责基于这些模版实例化一个粒子流。一个CAEmitterCell
类似于一个CALayer
:它有一个contents
属性可以定义为一个CGImage
,另外还有一些可设置属性控制着表现和行为。
CAEMitterCell:
-
color
:指定了一个可以混合图片内容颜色的混合色。 -
emitterSize
:发射源的大小,这个emitterSize结合position构建了发射源的位置及大小的矩形区域rect -
emitterShape
: 规定了发射源的形状。
kCAEmitterLayerPoint:点形状,发射源的形状就是一个点,位置在上面position设置的位置
kCAEmitterLayerLine:线形状,发射源的形状是一条线,位置在rect的横向的位于垂直方向中间那条
kCAEmitterLayerRectangle:矩形状,发射源是一个矩形,就是上面生成的那个矩形rect
kCAEmitterLayerCuboid:立体矩形形状,发射源是一个立体矩形,这里要生效的话需要设置z方向的数据,如果不设置就同矩形状
kCAEmitterLayerCircle:圆形形状,发射源是一个圆形,形状为矩形包裹的那个圆,二维的
kCAEmitterLayerSphere:立体圆形,三维的圆形,同样需要设置z方向数据,不设置则通二维一样
-
emitterMode
:发射模式,这个字段规定了在特定形状上发射的具体形式是什么
kCAEmitterLayerPoints:点模式,发射器是以点的形势发射粒子。
kCAEmitterLayerOutline:这个模式下整个边框都是发射点,即边框进行发射
kCAEmitterLayerSurface:这个模式下是我们边框包含下的区域进行抛洒
kCAEmitterLayerVolume:同上
-
emitterPosition
:发射位置。 -
birthRate
: 粒子产生系数,默认1.0 -
emitterDepth
:粒子的深度联系。 -
lifetime
: 粒子的生命周期。 -
spin
:自旋转速度。 -
birthrate
: 粒子参数的速度乘数因子;每秒发射的粒子数量 -
alphaSpeed
: 粒子消失的速度。 -
renderMode
:渲染模式,控制着在视觉上粒子图片是如何混合的
NSString * const kCAEmitterLayerUnordered;
NSString * const kCAEmitterLayerOldestFirst;
NSString * const kCAEmitterLayerOldestLast;
NSString * const kCAEmitterLayerBackToFront;
NSString * const kCAEmitterLayerAdditive;
- 某一属性的变换范围:emissionRange(值是2π,这意味着例子可以从360度任意位置反射出来。如果指定一个小一些的值,就可以创造出一个圆锥形)、velocityRange
- 指定值在时间线上的变化,例如:
alphaSpeed = 0.4
,说明粒子每过一秒减小0.4。
UIView * containView = [[UIView alloc]initWithFrame:self.view.bounds];
containView.center = self.view.center;
containView.backgroundColor = self.view.backgroundColor;
self.containView = containView;
[self.view addSubview:self.containView];
CAEmitterLayer *emitter = [CAEmitterLayer layer];
emitter.frame = self.containView.bounds;
[self.containView.layer addSublayer:emitter];
emitter.renderMode = kCAEmitterLayerAdditive;//这会让重叠的地方变得更亮一些。
emitter.emitterPosition = CGPointMake(emitter.frame.size.width / 2.0, emitter.frame.size.height / 2.0);
CAEmitterCell *cell = [[CAEmitterCell alloc] init];
cell.contents = (__bridge id)[UIImage imageNamed:@"star_yellow"].CGImage;
cell.birthRate = 150;
cell.lifetime = 5.0;
cell.color = [UIColor colorWithRed:1 green:0.5 blue:0.1 alpha:1.0].CGColor;
cell.alphaSpeed = -0.4;
cell.velocity = 50;
cell.velocityRange = 50;
cell.emissionRange = M_PI * 2.0;
emitter.emitterCells = @[cell];
效果:
flower.gifCAEAGLLayer(复杂)
在iOS 5中,苹果引入了一个新的框架叫做GLKit,它去掉了一些设置OpenGL的复杂性,提供了一个叫做
CLKView
的UIView
的子类,帮你处理大部分的设置和绘制工作。前提是各种各样的OpenGL绘图缓冲的底层可配置项仍然需要你用CAEAGLLayer
完成,它是CALayer
的一个子类,用来显示任意的OpenGL>图形。
AVPlayerLayer
最后一个图层类型是
AVPlayerLayer
。尽管它不是Core Animation框架的一部分(AV前缀看上去像),AVPlayerLayer
是有别的框架(AVFoundation)提供的,它和Core Animation紧密地结合在一起,提供了一个CALayer
子类来显示自定义的内容类型。
- 需要包含
#import <AVFoundation/AVFoundation.h>
头文件。
因为AVPlayerLayer
是CALayer
的子类,它继承了父类的所有特性。我们并不会受限于要在一个矩形中播放视频。
核心代码:
NSURL *URL = [[NSBundle mainBundle] URLForResource:@"IMG_0014" withExtension:@"MOV"];
//create player and player layer
AVPlayer *player = [AVPlayer playerWithURL:URL];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
//set player layer frame and attach it to our view
playerLayer.frame = self.containView.bounds;
[self.containView.layer addSublayer:playerLayer];
//play the video
[player play];
效果:(这里播放的是本地视频)
AVPlayer.gif
网友评论