美文网首页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