美文网首页iOS开发码农的日常之iOS开发iOS技术资料
《Core Animation》-- CALayer的一些子类

《Core Animation》-- CALayer的一些子类

作者: 妖精的菩萨 | 来源:发表于2017-06-02 17:14 被阅读69次

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.png

CATextLayer

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方法来返回子类的宿主图层。通过重写子类有关UILabelset方法来完成对宿主图层的一些属性设置。

+(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的方法使用一样。通过添加控件效果如下:

CATextLayer

CATransformLayer

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.jpeg

CAGradientLayer

用来生成两种或更多颜色平滑渐变的,采用硬件加速。

参考链接:http://www.jianshu.com/p/1c8ef3116b42

属性:
  • startPointendPoint:决定了渐变的方向,采用单位坐标系。
  • 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:指定了一个CATransform3D3D变换(这种情况下,下一图层的位移和旋转将会移动到圆圈的下一个点)。
  • instanceBlueOffsetinstanceGreenOffset:实现颜色的偏移变化。
栗子:(拷贝多个图层按照一定的弧度排列显示)
    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.jpeg

CAScrollLayer

从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子类叫做CAEmitterLayerCAEmitterLayer是一个高性能的粒子引擎,被用来创建实时例子动画如:烟雾,火,雨等等这些效果。

CAEmitterLayer看上去像是许多CAEmitterCell的容器,这些CAEmitierCell定义了一个例子效果。你将会为不同的例子效果定义一个或多个CAEmitterCell作为模版,同时CAEmitterLayer负责基于这些模版实例化一个粒子流。一个CAEmitterCell类似于一个CALayer:它有一个contents属性可以定义为一个CGImage,另外还有一些可设置属性控制着表现和行为。

CAEMitterCell:

更多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.gif

CAEAGLLayer(复杂)

在iOS 5中,苹果引入了一个新的框架叫做GLKit,它去掉了一些设置OpenGL的复杂性,提供了一个叫做CLKViewUIView的子类,帮你处理大部分的设置和绘制工作。前提是各种各样的OpenGL绘图缓冲的底层可配置项仍然需要你用CAEAGLLayer完成,它是CALayer的一个子类,用来显示任意的OpenGL>图形。

AVPlayerLayer

最后一个图层类型是AVPlayerLayer。尽管它不是Core Animation框架的一部分(AV前缀看上去像),AVPlayerLayer是有别的框架(AVFoundation)提供的,它和Core Animation紧密地结合在一起,提供了一个CALayer子类来显示自定义的内容类型。

  • 需要包含#import <AVFoundation/AVFoundation.h> 头文件。

因为AVPlayerLayerCALayer的子类,它继承了父类的所有特性。我们并不会受限于要在一个矩形中播放视频。

核心代码:

    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

相关文章

网友评论

    本文标题: 《Core Animation》-- CALayer的一些子类

    本文链接:https://www.haomeiwen.com/subject/bbpufxtx.html