美文网首页
Core Animation 二 : CALayer的子类,大图

Core Animation 二 : CALayer的子类,大图

作者: Trigger_o | 来源:发表于2020-12-16 12:56 被阅读0次

一:CATiledLayer

继承自CALayer,它能够实现异步的分区的绘制,并且只绘制屏幕显示的范围,在特定的场景下非常好用.使用也很简单

//在自定义View中重写layerClass
+(Class)layerClass{
    return [CATiledLayer class];
}

//在init中设置layer的属性
    CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
    tiledLayer.levelsOfDetail = 1;
    tiledLayer.levelsOfDetailBias = ceil(log2(1/scale))+1;
    tiledLayer.tileSize = CGSizeMake(50, 50);

levelsOfDetail和levelsOfDetailBias是关于图片和layer尺寸之间的关系,假设图片铺满layer,假设一开始layer是100x100,那么在retina屏上最多就是300x300个像素,去显示一个10000x10000的位图时,会舍弃大部分的像素,这时将layer放大到300x300,如果不重绘就仍然是那些像素,就会模糊
levelsOfDetailBias是layer放大时,图片放大的次数,每增加1,就放大一倍,也就是平方.重绘时需要根据layer的rect来设置.
levelsOfDetail是layer缩小时,达到重绘的节点,设置为1,那么缩小layer就不会引起重绘;
详细的介绍

效果

二:CAGradientLayer

CAGradientLayer实现了渐变色功能,是CALayer子类,使用非常简单快捷,但是它和Core Graphics的一系列渐变色API应该是没有关系的,因为CALayer在Core Animation框架内

CAGradientLayer *gl = [CAGradientLayer layer];
    gl.startPoint = CGPointMake(0, 0);
    gl.endPoint = CGPointMake(1, 1);
    gl.colors = @[(__bridge id)UIColor.blackColor.CGColor,(__bridge id)[UIColor.blackColor colorWithAlphaComponent:.5].CGColor,(__bridge id)[UIColor.blackColor colorWithAlphaComponent:.0].CGColor];
    gl.locations = @[@0.25,@.75,@1.0];
    gl.bounds = layer.bounds;
    gl.anchorPoint = CGPointMake(0, 0);
    gl.position = CGPointMake(0, 0);
    //gl.type = kCAGradientLayerRadial;
    [layer addSublayer:gl];

1.这里直接创建了一个CAGradientLayer,渐变一般创建出来使用比较多,当然也可以重写+ (Class)layerClass来作为root layer
2.colors提供渐变的几个节点颜色,这里使用黑色的不同透明度
3.locations提供每个节点在什么位置开始生效
4.startPoint和endPoint提供渐变的起始和结束点,并不是真正的点,是类似锚点,取值在0-1,这两个参数决定了渐变产生的方向,两点连成的线是垂直于渐变的rgba等值线


分别是(0,0)(1,.5);(0,0)(1,1);(.5,0)(1,1)的效果

5.type默认是kCAGradientLayerAxial,rgba等值线是直线,kCAGradientLayerRadial的rgba等值线是锥形,kCAGradientLayerConic是带有弧度的,依据(0,0)startpoint和(0,0)endpoint两个向量去做弧形渐变,只能在iOS12以上使用,


kCAGradientLayerRadial和kCAGradientLayerConic

三: CAShapeLayer

CAShapeLayer实现了绘图的功能,它的核心就是给CGPath一个容器,使用时最关键的内容就是构建CGPath
CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,它是矢量图形,而不是bitmap,所以无论有多大,都不会占用太多的内存。
CAShapeLayer可以在边界之外绘制,图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉.

 CAShapeLayer *sl = [CAShapeLayer layer];
    sl.path = CGPathCreateWithRoundedRect(CGRectMake(50, 50, 100, 100), 15, 15, nil);
    sl.strokeColor = UIColor.redColor.CGColor;
    sl.fillColor = UIColor.clearColor.CGColor;
    sl.lineWidth = 5;
    [self.view.layer addSublayer:sl];
image.png

只要是CGPath就行,当然用UIBezierPath构建路径也可以.

三: CATextLayer

看名字就知道是用来显示文本的,CATextLayer使用了Core text,它的性能比UILabel更强,当需要绘制大量文字的时候,可以选择使用CATextLayer,比如文章,电子书.

NSString *str = @"CATextLayer disables sub-pixel antialiasing when rendering text. Text can only be drawn using sub-pixel antialiasing when it is composited into an existing opaque background at the same time that it's rasterized. There is no way to draw text with sub-pixel antialiasing by itself, whether into an image or a layer, in advance of having the background pixels to weave the text pixels into. Setting the opacity property of the layer to YES does not change the rendering mode.";

    CATextLayer *tl = [CATextLayer layer];
    tl.frame = CGRectMake(20, 100, 400, 400);
    [self.view.layer addSublayer:tl];
    tl.contentsScale = UIScreen.mainScreen.scale;
    tl.wrapped = YES;
    tl.alignmentMode = kCAAlignmentLeft;
    tl.foregroundColor = UIColor.orangeColor.CGColor;
    UIFont *font = [UIFont systemFontOfSize:16];
    CFStringRef fontName = (__bridge CFStringRef)font.fontName;
    CGFontRef fontref = CGFontCreateWithFontName(fontName);
    tl.font = fontref;
    tl.fontSize = font.pointSize;
    CGFontRelease(fontref);
    NSMutableAttributedString *att = [[NSMutableAttributedString alloc]initWithString:str];
    NSMutableParagraphStyle *ps = [NSMutableParagraphStyle new];
    ps.lineSpacing = 5;
    [att addAttributes:@{NSParagraphStyleAttributeName : ps,NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:UIColor.orangeColor} range:NSMakeRange(0, att.length)];
//    tl.string = att;
    tl.string = str;

1.contentsScale是物理像素和逻辑point的关系,也就是1x2x3x
2.wrapped是换行
3.alignmentMode是布局模式,对标UIlalbel的textAlignment
4.foregroundColor是字体颜色
5.CATextLayer的font需要的是CGFontRef
6.string是id类型,可以是NSString也可以是NSAttributedString,当使用NSString时,上面的45,也就是颜色和font两个属性才会生效,使用NSAttributedString的时候颜色和font只会从富文本的Attributes中取,如果没有则是黑色和默认字体大小.

四: CALayer的子类结合mask

写一个渐变文本的例子

    CATextLayer *tl = [CATextLayer layer];
    tl.contentsScale = UIScreen.mainScreen.scale;
    tl.wrapped = YES;
    tl.alignmentMode = kCAAlignmentLeft;
    tl.foregroundColor = UIColor.orangeColor.CGColor;
    NSMutableAttributedString *att = [[NSMutableAttributedString alloc]initWithString:str];
    NSMutableParagraphStyle *ps = [NSMutableParagraphStyle new];
    ps.lineSpacing = 5;
    [att addAttributes:@{NSParagraphStyleAttributeName : ps,NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:UIColor.orangeColor} range:NSMakeRange(0, att.length)];
    tl.string = att;
    
    CAGradientLayer *gl = [CAGradientLayer layer];
    gl.startPoint = CGPointMake(0, 0);
    gl.endPoint = CGPointMake(1, 1);
    gl.colors = @[(__bridge id)UIColor.redColor.CGColor,(__bridge id)UIColor.orangeColor.CGColor,(__bridge id)UIColor.yellowColor.CGColor];
    gl.locations = @[@0.2,@.6,@1.0];
    gl.type = kCAGradientLayerAxial;
    tl.frame = CGRectMake(0, 0, ScreenWidth-40, 400);
    gl.frame = (CGRect){20,100,tl.frame.size};
    [gl setMask:tl];
    [self.view.layer addSublayer:gl];

gl是一个渐变的色块,被添加到界面上,这时候给gl添加一个mask,mask的显示原则是alpha通道过滤,在mask完全透明的位置,对应gl的部分则完全不可见,mask完全不透明的部分,对应gl的部分则正常显示,这个例子mask是CATextLayer,其中文字是完全不透明的,非文字的区域是透明的,最终效果就是渐变层被纹上了文字一般,而且文字颜色是渐变层自己的颜色.
需要注意gl和tl的frame,或者设置anchorPoint和position,mask需要完全贴合layer,所以tl的origin是0,0,size和gl一样


image.png

五:CAReplicatorLayer

CAReplicatorLayer可以copy已有的layer,形成一组重复的layer,并且可以定义这一组layer的变换规律,甚至可以借此制作动画效果
1.实现镜像效果

+ (Class)layerClass{
    return CAReplicatorLayer.class;
}

- (instancetype)initWithFrame:(CGRect)frame{
    if(self = [super initWithFrame:frame]){
        CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
        layer.instanceCount = 2;
        CATransform3D transform = CATransform3DIdentity;
        CGFloat verticalOffset = self.bounds.size.height + 2;
        transform = CATransform3DTranslate(transform, 0, verticalOffset, 0);
        transform = CATransform3DScale(transform, 1, -1, 0);
        layer.instanceTransform = transform;
        layer.instanceAlphaOffset = -0.6;
    }
    return self;
}


TestView *tv = [[TestView alloc]initWithFrame:CGRectMake(100, 100, 200, 200)];
    [self.view addSubview:tv];
    
    _imgv = UIImageView.new;
    _imgv.image = [UIImage imageNamed:@"avatar"];
    _imgv.frame = CGRectMake(0, 0, 200, 200);
    [tv addSubview:_imgv];

自定义了一个TestView,在构造方法中设置CAReplicatorLayer的属性
instanceCount是重复个数
instanceTransform是变换效果
instanceAlphaOffset是透明度


image.png

被复制的layer并不是真正的layer,只是绘制效果(图形),最终只有一个layer,第二个图形根据第一个图形,按照instanceTransform进行变换,第三个图形会在第二个的基础上进行变换
改造一下上面的代码再看看

CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
        layer.instanceCount = 3;
        CATransform3D transform = CATransform3DIdentity;
        CGFloat verticalOffset = self.bounds.size.height + 2;
        transform = CATransform3DTranslate(transform, 0, verticalOffset, 0);
//        transform = CATransform3DScale(transform, 1, -1, 0);
        transform = CATransform3DRotate(transform, M_PI_4, 0, 0, 1);
        layer.instanceTransform = transform;
        layer.instanceAlphaOffset = -0.2;
image.png

可以看到,第二张旋转了45度,但是第三张在第二张的基础上旋转,因此被旋转了90度.

2.制作动画

        CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
        layer.instanceCount = 10;
        CATransform3D transform = CATransform3DIdentity;
        transform = CATransform3DRotate(transform, M_PI/5, 0, 0, 1);
        layer.instanceTransform = transform;
        layer.instanceAlphaOffset = -0.05;
        layer.instanceDelay = .15;

TestView *tv = [[TestView alloc]initWithFrame:CGRectMake(0, 100, 414, 414)];
    [self.view addSubview:tv];
    
    _imgv = UIImageView.new;
    _imgv.image = [UIImage imageNamed:@"avatar"];
    _imgv.frame = CGRectMake(190, 0, 34, 34);
    [tv addSubview:_imgv];

改造一下刚才的代码,copy10次,每个都比上一个多旋转36度,在testView上add一张图,放在上面的位置,_imgv.frame = CGRectMake(190, 0, 34, 34);每当copy并旋转一次,就多一个image,这里需要注意的是,instanceTransform是写在TestView里的,也就是说,旋转的是TestView的layer本身,而不是image,所以最终形成表盘一样的效果.


image.png

接下来给image添加一个动画

_imgv.transform = CGAffineTransformScale(CGAffineTransformIdentity, .1, .1);
    CABasicAnimation *an2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    an2.fromValue = @(1.0);
    an2.toValue = @(0.1);
    an2.cumulative = YES;
    an2.repeatCount = CGFLOAT_MAX;
    an2.duration = 1.5;
    an2.fillMode = kCAFillModeForwards;
    an2.removedOnCompletion = NO;
    [_imgv.layer addAnimation:an2 forKey:@"an2"];

在添加动画之前,先修改_imgv的大小,改成最小状态,看起来更舒服

只是这样的话,10个小图片会同时动画,CAReplicatorLayer还提供了一个很强的属性,instanceDelay,它可以使被copy的图形按照延迟来执行动画,和instanceTransform,instanceAlphaOffset一样都是递增的.


gif

相关文章

网友评论

      本文标题:Core Animation 二 : CALayer的子类,大图

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