美文网首页
iOS CoreAnimation(二) - CALayer/U

iOS CoreAnimation(二) - CALayer/U

作者: 顶级蜗牛 | 来源:发表于2024-03-28 16:17 被阅读0次

相关文献:
iOS CoreAnimation(一) - 基础知识
iOS CoreAnimation(二) - CALayer/UIBezierPath
iOS CoreAnimation(三) - CAEmitterLayer粒子图层

本文主要内容:
1.CALayer的属性
2.仿射变换、透视投影、正背面渲染
3.专有图层
4.贝塞尔曲线API

专有图层

一、CALayer的属性

1.contents给UIView设置图片

通过UIView的layer.contents来展示image,而不用再创建一个uIImageView去展示图片:

    CALayer *layer = [CALayer layer];
    layer.backgroundColor = [UIColor clearColor].CGColor;
    UIImage *image = [UIImage imageNamed:@"AppIcon120x120"];
//    contents:需要用__bridge id桥接一下MAC OS遗留的问题
    layer.contents = (__bridge id)image.CGImage;
    layer.frame = CGRectMake(20, 20, 100, 100);
    [self.subView.layer addSublayer:layer];
2.contentsGravity设置contents的图片填充效果

layer.contentsGravity 和 view.contentModel类似,是为了设置contents拉伸效果准备:

//填充效果
// self.subView.contentMode = UIViewContentModeScaleAspectFit;
self.subView.layer.contentsGravity = kCAGravityResizeAspect;

kCAGravityCenter:按照图片像素展示,图片中心点和layer中心点相同
kCAGravityTop:按照图片像素展示,图片x轴中心与layer相同,底部和layer底部对齐
kCAGravityBottom:按照图片像素展示,图片x轴中心与layer相同,顶部和layer顶部对齐
kCAGravityLeft:按照图片像素展示,图片y轴中心与layer相同,左边和layer左边对齐
kCAGravityRight:按照图片像素展示,图片y轴中心与layer相同,右边和layer右边对齐
kCAGravityTopLeft:按照图片像素展示,图片左下角和layer左下角对齐
kCAGravityTopRight:按照图片像素展示,图片右下角和layer右下角对齐
kCAGravityBottomLeft:按照图片像素展示,图片左上角和layer左上角对齐
kCAGravityBottomRight:按照图片像素展示,图片右上角和layer右上角对齐
kCAGravityResize:填充满layer,拉伸图片
kCAGravityResizeAspect:按照layer的最短边充满layer
kCAGravityResizeAspectFill:按照layer的最长边充满layer,不拉伸图片
kCAGravityCenter kCAGravityResize-默认 kCAGravityResizeAspectFill kCAGravityTop kCAGravityBottom kCAGravityLeft kCAGravityRight kCAGravityTopLeft

... ...

3.contentsScale避免Retina屏幕显示错误

当⽤代码设置contents图⽚时,要⼿动设置图层的layer.contentsScale的属性,避免Retina屏幕显示错误。

self.subView.layer.contentsScale = [[UIScreen mainScreen] scale];

contentsScale定义了寄宿图的像素尺寸和视图大小的比例:

  • a. 设置为1.0(默认),将会以每个点1个像素绘制图片;
  • b. 设置为 2.0,则会以每个点2个像素绘制图片,这就是我们熟知的Retina屏幕;
  • c. 当contentsGravity 等于 (kCAGravityResize || kCAGravityResizeAspect || kCAGravityResizeAspectFill) 时候,设置该属性没有任何效果, 因为kCAGravityResizeAspect就是拉伸 图片以适应图层而已,根本不会考虑到分辨率问题。
    但是如果我们把 contentsGravity 设置为kCAGravityCenter这种不会拉伸图片的值的话,图片展示的大小就是图片像素除以contentsScale的大小。
4.contentsRect
点/像素/单位 等区别

contentsRect默认是{0, 0, 1, 1}
contentsRect会比contentsGravity得到的效果显得更加灵活。

5.makeToBounds是否显示超出边界的内容

layer.makeToBounds 和 view.clipsToBounds类似,是否显示超出边界的内容,默认YES。

6.zPosition设置layer深度
-(void)testZPosition{
    CALayer *layer1 = [CALayer layer];
    layer1.backgroundColor = [UIColor redColor].CGColor;
    layer1.frame = CGRectMake(20, 20, 100, 100);
    [self.subView.layer addSublayer:layer1];
    self.layer1 = layer1;

    CALayer *layer2 = [CALayer layer];
    layer2.backgroundColor = [UIColor yellowColor].CGColor;
    layer2.frame = CGRectMake(40, 40, 100, 100);
    [self.subView.layer addSublayer:layer2];
    self.layer2 = layer2;

    layer1.zPosition = 1;
}

正常情况下layer1在layer2的下面,但是当把layer1的zPosition设置为1的时候,layer1就在layer2的上面。

layer.zPosition代表Z轴的坐标(深度,默认是0),值越大代表越在上面。
注意:zPosition并不会去更改层级关系!

7.delegate: layer也有自己的代理CALayerDelegate
 self.layer.delegate = self;

当设置好代理后,通过代码 [self.layer display]; 系统会自动判断是否重写实现了displayLayer方法,若实现了则调用,若没有实现则调用drawLayer: inContext

-(void)displayLayer:(CALayer *)layer{

    static int i = 0;
    layer.frame = CGRectMake(0, 0, layer.frame.size.width + 10, layer.frame.size.height);
    NSLog(@"%@   %@",layer,self.layer);
    if (i == 0) {
        layer.backgroundColor = [UIColor yellowColor].CGColor;
    }else{
        layer.backgroundColor = [UIColor orangeColor].CGColor;
    }
    i++;
}

-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
    //draw a thick red circle
    CGContextSetLineWidth(ctx, 10.0f);
    CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
    CGContextStrokeEllipseInRect(ctx, layer.bounds);
}
8.hitTest核心动画一已经介绍过了。

二、仿射变换

1.仿射变换原理
CATransform3D本身就是个矩阵
// 旋转
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z) 
// 缩放
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) 
// 平移
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)

仿射变换的原理就是图形上的每一个顶点各自都乘上想要变换的变换矩阵,就能得到变换后的顶点。(变换矩阵可能是旋转矩阵/缩放矩阵/平移矩阵)

比如我们想要缩放一个视图,就让该视图上每一个 顶点 * 缩放矩阵 = 缩放后的顶点

变换矩阵可以是这张图

变换矩阵是如何推演出来的呢?
首先3D图形也是由很多个2D的平面图形组合成的,并且两个向量p和q共同组成了一个平面图形,当图形旋转时,p和q一起同时旋转。
通过三角函数就能得到旋转后的p和q的位置:

2D的旋转

3D图形则有三个向量p/q/r共同组成,其原理和2D图形是一样的。
下图是围绕X轴旋转,故而X轴对应的p不变。

围绕X轴的3D旋转 围绕Y轴的3D旋转 围绕Z轴的3D旋转

一般来说可以使用 CoreAnimation 提供的如下函数来创建三维变换矩阵:

判断t矩阵是否为单位矩阵
CATransform3DIsIdentity(CATransform3D t);
判断两个变换矩阵是否相等
CATransform3DEqualToTransform(CATransform3D a, CATransform3D b);
创建在x方向上移动tx,在y方向上移动ty,在z方向上移动tz的变换矩阵。
CATransform3DMakeTranslation(CGFloat tx, CGFloat ty, CGFloat tz);
创建在x方向上缩放tx,在y方向上缩放ty,在z方向上缩放tz的变换矩阵。
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz);
创建基于指定旋转轴旋转angle弧度的变换,其中x,y,z用于确定旋转轴的方向。
比如 (1,0,0)指定旋转轴为x轴,(1,1,0)指定以x,y轴夹角的中线为旋转轴。
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z);
以已有t变换矩阵为基础进行位移变换。
CATransform3DTranslate(CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz);
以已有t变换矩阵为基础进行缩放变换。
CATransform3DScale(CATransform3D t, CGFloat sx, CGFloat sy, CGFloat sz);
以已有t变换矩阵为基础进行旋转变换。
CATransform3DRotate(CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z);
对a变换矩阵进行累加
CATransform3DConcat(CATransform3D a, CATransform3D b);
对已有的t变换矩阵执行反转。
CATransform3DInvert(CATransform3D t);

将CGAffineTransform矩阵包装成为CATransform3D变换矩阵,该CATransform3D也只有x,y维度变换。
CATransform3DMakeAffineTransform(CGAffineTransform m);
如果t变换矩阵只有一个CGAffineTransform矩阵,则改函数返回YES.
CATransform3DIsAffine(CATransform3D t) ;
获取t变换矩阵所包含的CGAffineTransform变换矩阵。
CATransform3DGetAffineTransform(CATransform3D t) ;
2.透视投影

执行下面代码,让视图绕Y轴旋转90度:

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *layerView;
@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (weak, nonatomic) IBOutlet UIImageView *layerView1;
@property (weak, nonatomic) IBOutlet UIImageView *layerView2;

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    /*
     参数2: 角度(弧度为单位,如果是度数需要做转化)
     参数3:  x, (1=旋转,0=不旋转)
     参数4:  y,
     参数5:  z,
    */
    CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    self.layerView.layer.transform = transform;
}
@end

会发现视图并没有想象中的那种旋转,甚至图片变窄失真了;
这是因为投影方式:有2种:正投影(默认),透视投影。

投影方式

只需要对矩阵中的m34为进行调整即可为透视投影:

    //投影方式: 2种.正投影,透视投影;
    //CATransform3DIdentity 单元矩阵
    CATransform3D transform1 = CATransform3DIdentity;
    transform1.m34 = -1.0/500.0; // 投影举例 -1.0/500.0  或 -1.0/1000.0
   
    /*
     CATransform3D CATransform3DRotate (CATransform3D t, CGFloat angle,
     CGFloat x, CGFloat y, CGFloat z)
     参数1: t,矩阵CATransform3D
     参数2: 角度(弧度为单位,如果是度数需要做转化)
     参数3:  x, (1=旋转,0=不旋转)
     参数4:  y,
     参数5:  z,
     */
    transform1 = CATransform3DRotate(transform1, M_PI/4, 0, 1, 0);
    self.layerView.layer.transform = transform1;

若出现子视图旋转需要透视投影,应该如何做?

设置父视图的sublayerTransform

    /**
     当只对子图层进行仿射变换时,设置sublayerTransform,再对子图层transform进行处理
     */
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0/500;
    //sublayerTransform 子图层上仿射变换(会影响添加在此图层上的子图层)
    self.containerView.layer.sublayerTransform = perspective;
    
    CATransform3D transform2 = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
    CATransform3D transform3 = CATransform3DMakeRotation(-M_PI_4, 0, 1, 0);
    
    self.layerView1.layer.transform = transform2;
    self.layerView2.layer.transform = transform3;
透视投影实现立体盒子
xib

通过透视投影要实现这样一个正方体:

#import "CCViewController.h"

@interface CCViewController ()
@property (weak, nonatomic) IBOutlet UIView *containerView; // 父视图
// 子视图
@property (strong, nonatomic) IBOutlet UIView *view0;
@property (strong, nonatomic) IBOutlet UIView *view1;
@property (strong, nonatomic) IBOutlet UIView *view2;
@property (strong, nonatomic) IBOutlet UIView *view3;
@property (strong, nonatomic) IBOutlet UIView *view4;
@property (strong, nonatomic) IBOutlet UIView *view5;

@property(nonatomic,strong)NSArray *faces;
@end

@implementation CCViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.faces = @[_view0,_view1,_view2,_view3,_view4,_view5];
    
    //父View的layer图层
    CATransform3D perspective = CATransform3DIdentity;
    perspective.m34 = -1.0 / 500.0;
    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);
    perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
    self.containerView.layer.sublayerTransform = perspective;
    
    //add cube face 1
    CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);
    [self addFace:0 withTransform:transform];
    
    //add cube face 2
    transform = CATransform3DMakeTranslation(100, 0, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);
    [self addFace:1 withTransform:transform];
    
    //add cube face 3
    transform = CATransform3DMakeTranslation(0, -100, 0);
    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);
    [self addFace:2 withTransform:transform];
    
    //add cube face 4
    transform = CATransform3DMakeTranslation(0, 100, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);
    [self addFace:3 withTransform:transform];
    
    //add cube face 5
    transform = CATransform3DMakeTranslation(-100, 0, 0);
    transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);
    [self addFace:4 withTransform:transform];
    
    //add cube face 6
    transform = CATransform3DMakeTranslation(0, 0, -100);
    transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);
    [self addFace:5 withTransform:transform];
    
}
- (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform {
    //获取face视图并将其添加到容器中
    UIView *face = self.faces[index];
    [self.containerView addSubview:face];
    
    //将face视图放在容器的中心
    CGSize containerSize = self.containerView.bounds.size;
    face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);
    
    //添加transform
    face.layer.transform = transform;
}
@end
3.正背面渲染

倘若我把视图绕Y轴旋转180度,结果就是原始图的翻转效果,并不会出现看不见视图的情况。默认开启正背面渲染。
但是如果是针对立体图形的情况,比如正方体,它的每个面都不需要显示出正背面渲染。

    CATransform3D transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);
    self.layerView.layer.transform = transform;
    //正背面渲染--> 渲染技术正背面剔除
    self.layerView.layer.doubleSided = NO;
4.透视投影案例
#import "ViewController.h"
#define DEGREES_TO_RADIANS(d) (d * M_PI / 180) // 度数转弧度
@interface ViewController ()
@property (nonatomic, strong) CALayer *rootLayer;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.rootLayer = [CALayer layer];
    // 应用透视转换
    CATransform3D transform = CATransform3DMakePerspective(1000);
    self.rootLayer.sublayerTransform = transform;
    self.rootLayer.frame = self.view.bounds;
    [self.view.layer addSublayer:self.rootLayer];
    
    //颜色数组
    NSArray *colors = @[[UIColor colorWithRed:0.263 green:0.769 blue:0.319 alpha:1.000],
                        [UIColor colorWithRed:0.990 green:0.759 blue:0.145 alpha:1.000],
                        [UIColor colorWithRed:0.084 green:0.398 blue:0.979 alpha:1.000]];
    
    //添加3个图层
    [self addLayersWithColors:colors];
    
    // 延时1秒,执行动画
    [self performSelector:@selector(rotateLayers) withObject:nil afterDelay:1.0];
}

- (void)addLayersWithColors:(NSArray *)colors {
    //遍历颜色数组,创建图层
    for (UIColor *color in colors) {
        //创建图层
        CALayer *layer = [CALayer layer];
        //颜色
        layer.backgroundColor = color.CGColor;
        //大小
        layer.bounds = CGRectMake(0, 0, 200, 200);
        //位置
        layer.position = CGPointMake(160, 190);
        //透明度
        layer.opacity = 0.80;
        //圆角
        layer.cornerRadius = 10;
        //边框颜色
        layer.borderColor = [UIColor whiteColor].CGColor;
        //边框宽度
        layer.borderWidth = 1.0;
        //阴影offset ,默认(0,3)
        layer.shadowOffset = CGSizeMake(0, 2);
        //用于创建阴影的模糊半径。默认值为3。可动画的
        layer.shadowOpacity = 0.35;
        //阴影颜色
        layer.shadowColor = [UIColor darkGrayColor].CGColor;
        //是否光栅化
        layer.shouldRasterize = YES;
        //添加图层
        [self.rootLayer addSublayer:layer];
    }
}

- (void)rotateLayers {
    //创建基本动画以围绕Y轴和Z轴旋转
    CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
    transformAnimation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
    // 沿X轴Z轴旋转85度
    transformAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(DEGREES_TO_RADIANS(85), 0, 1, 1)];// DEGREES_TO_RADIANS 度数转弧度
    transformAnimation.duration = 1.5;
    //自动翻转
    transformAnimation.autoreverses = YES;
    //重复次数
    transformAnimation.repeatCount = HUGE_VALF;  // HUGE_VALF 巨大的数
    
    //定义动画步调的计时函数
    transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; // 时机:快进快出
    
    int tx = 0;
    // 循环浏览子图层并附加动画
    for (CALayer *layer in [self.rootLayer sublayers]) {
        //为图层添加动画
        [layer addAnimation:transformAnimation forKey:nil];
        
        // 创建要沿X轴平移的动画
        CABasicAnimation *translateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
        translateAnimation.fromValue = [NSValue valueWithCATransform3D:layer.transform];
        translateAnimation.toValue = [NSNumber numberWithFloat:tx];
        translateAnimation.duration = 1.5;
        translateAnimation.autoreverses = YES;
        translateAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        translateAnimation.repeatCount = HUGE_VALF;
        [layer addAnimation:translateAnimation forKey:nil];
        tx += 35;
    }
}

//透视投影修改m34值
static CATransform3D CATransform3DMakePerspective(CGFloat z) {
    CATransform3D t = CATransform3DIdentity;
    t.m34 = -1.0 / z;
    return t;
}
@end

三、CAShapeLayer(⽤于绘制⽴体⻉塞尔曲线)

  • 渲染快速CAShapeLayer 使⽤了硬件加速,绘制同⼀图形会⽐⽤ Core Graphics 快很多。
  • ⾼效使⽤内存CAShapeLayer 不需要像普通 CALayer ⼀样创建⼀个寄宿图形,所以⽆论有多⼤,都不会占⽤太多的内存。
  • 不会被图层边界剪裁掉CAShapeLayer 可以在边界之外绘制。你的图层路径不会像在使⽤ Core Graphics 的普通CALayer ⼀样被剪裁掉。
  • 不会出现像素化。当你给 CAShapeLayer 做3D变换时,它不像⼀个有寄宿图的普通图层⼀样变得像素化。

用代码画一个火柴人、给layer添加部分圆角:

#import "ViewController.h"
@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // 画个小柴人
    UIBezierPath *path = [[UIBezierPath alloc]init];
    [path moveToPoint:CGPointMake(175, 100)];
    //画个圆
    [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];
    [path moveToPoint:CGPointMake(150, 125)];
    
    [path addLineToPoint:CGPointMake(150, 175)];
    [path addLineToPoint:CGPointMake(175, 225)];
    
    [path moveToPoint:CGPointMake(100, 150)];
    [path addLineToPoint:CGPointMake(200, 150)];
    
    // 给layer圆角
    CGRect rect = CGRectMake(50, 50, 100, 100);
    CGSize radii = CGSizeMake(20, 20);
    UIRectCorner corners1 = UIRectCornerBottomRight | UIRectCornerBottomLeft; // 下圆角
    UIRectCorner corners2 = UIRectCornerTopRight | UIRectCornerTopLeft; // 上圆角
    UIBezierPath *path2 = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners2 cornerRadii:radii];
    
    //1.create Shaper Layer
    CAShapeLayer *shaperLayer = [CAShapeLayer layer];
    //2.画笔颜色
    shaperLayer.strokeColor = [UIColor redColor].CGColor;
    //3.fillcolor
    shaperLayer.fillColor = [UIColor purpleColor].CGColor;
    //4.线段的宽度
    shaperLayer.lineWidth = 5;
    //5.链接方法
    shaperLayer.lineJoin = kCALineJoinRound;
    shaperLayer.lineCap =kCALineCapRound;
    
//    shaperLayer.path = path1.CGPath;
    shaperLayer.path = path2.CGPath;
    [self.view.layer addSublayer:shaperLayer];
}
@end

四、CATextLayer (用户绘制AttributeString)

如果你想在⼀个图层⾥⾯显示⽂字,完全可以借助图层代理直接将字符串使⽤CATextLayer写⼊图层的内容。

#import "ViewController.h"
@interface ViewController ()
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //create a text layer
    CATextLayer *textLayer = [CATextLayer layer];
    textLayer.frame = CGRectMake(10, 100, self.view.frame.size.width-20,200);
    //字体颜色
    textLayer.foregroundColor = [UIColor blackColor].CGColor;
    //对齐方式
    textLayer.alignmentMode = kCAAlignmentCenter;
    //环绕
    textLayer.wrapped = YES;

    //选择字体
    UIFont *font = [UIFont systemFontOfSize:15];
    //将font -> 图层字体
    CFStringRef fontName = (__bridge CFStringRef)font.fontName;
    CGFontRef fonRef = CGFontCreateWithFontName(fontName);
    textLayer.font = fonRef;
    textLayer.fontSize = font.pointSize;
    CGFontRelease(fonRef);
    
    //内容
    NSString *text = @"Hello, I'm Jokn.";
    //文本像素化的原因,没有以Retina 渲染,默认等于=1.需要传合适的值
    textLayer.contentsScale = [UIScreen mainScreen].scale;
    textLayer.string = text;
    
    [self.view.layer addSublayer:textLayer];
}
@end

亦可以通过CATextLayer去重写UILabel以达到更快:

#import <UIKit/UIKit.h>
@interface LayerLabel : UILabel
@end
#import "LayerLabel.h"
@implementation LayerLabel

+(Class)layerClass {
    return [CATextLayer class];
}

-(CATextLayer *)textLayer {
    return (CATextLayer *)self.layer;
}

-(id)initWithFrame:(CGRect)frame {
    if(self = [super initWithFrame:frame]) {
        [self setUp];
    }
    return self;
}

- (void)awakeFromNib {
    [self setUp];
}

- (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;
    //将font -> 图层字体
    CFStringRef fontName = (__bridge CFStringRef)font.fontName;
    CGFontRef fonRef = CGFontCreateWithFontName(fontName);
    [self textLayer].font = fonRef;
    [self textLayer].fontSize = font.pointSize;
    CGFontRelease(fonRef);
}

-(void)setUp {
    self.text = self.text;
    self.textColor = self.textColor;
    self.font = self.font;
    
    [self textLayer].alignmentMode = kCAAlignmentCenter;
    [self textLayer].wrapped = YES;
    [self.layer display];
}
@end

五、CATransformLayer (渲染3D层次结构)

CATransformLayer是⼀个专⻔⽤来创建三维视图的⼀个layer。

做一个如下图一样的正方体案例:

#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIView *containerView; // 拖拽一个全屏视图
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blackColor];
    
    //如果大家想要有一个立体的投影效果.必须要设置投影方式,否则的CATransformLayer
    CATransform3D pt = CATransform3DIdentity;
    pt.m34 = -1.0/500;
    self.containerView.layer.sublayerTransform = pt;
    
    // 绘制第一个
    CATransform3D c1 = CATransform3DIdentity;
    c1 = CATransform3DTranslate(c1, -100, 0, 0);
    CALayer *cube1 = [self cubeWithTransform:c1];
    [self.containerView.layer addSublayer:cube1];
    
    // 绘制第二个
    CATransform3D c2 = CATransform3DIdentity;
    c2 = CATransform3DTranslate(c2, 100, 0, 0);
    c2 = CATransform3DRotate(c2, -M_PI_4, 1, 0, 0); // 旋转一下,看一下别的面有没有添加成功
    c2 = CATransform3DRotate(c2, -M_PI_4, 0,1, 0);
    CALayer *cube2 = [self cubeWithTransform:c2];
    [self.containerView.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.containerView.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 {
    //1.创建子layer
    CALayer *face = [CALayer layer];
    face.frame = CGRectMake(-50, -50, 100, 100);
    
    //颜色
    CGFloat red = (rand()/(double)INT_MAX);
    CGFloat green = (rand()/(double)INT_MAX);
    CGFloat blue = (rand()/(double)INT_MAX);
    
    face.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;
    face.transform  = transform;
    
    return face;
}
@end

六、CAGradientLayer (图层颜色渐变)

CAGradientLayer是⽤来⽣成两种或更多种颜⾊平滑渐变。

- (void)viewDidLoad {
    [super viewDidLoad];
    //starPoint,endPoint 决定渐变方向;默认左上角(0,0),右下角坐标(1,1);
    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = CGRectMake(10, 50, self.view.frame.size.width-20, 100);
    gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor,(__bridge id)[UIColor greenColor].CGColor,(__bridge id)[UIColor blueColor].CGColor];
    
    //location,色块位置;
    gradientLayer.locations = @[@0.25,@0.5,@0.25];

    //左上角的位置
    gradientLayer.startPoint = CGPointMake(0, 0);
    //右下角的位置
    gradientLayer.endPoint = CGPointMake(1, 1);
    
    [self.view.layer addSublayer:gradientLayer];
}

七、CAReplicatorLayer (⽣成相似图层)

CAReplicatorLayer为了⾼效⽣成许多相似的图层.他会绘制⼀个或者多个图层的⼦图层.并在每个复制体上应⽤不同的变换.

给一张照片生成倒影:

#import "ReflectionView.h"
@implementation ReflectionView

+ (Class)layerClass {
    return [CAReplicatorLayer class];
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self setUp];
    }
    return self;
}

- (void)awakeFromNib {
    [self setUp];
}

-(void)setUp {
    CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
    layer.instanceCount = 2;
    
    CATransform3D transform = CATransform3DIdentity;
    //间隔
    CGFloat  veticalOffset = self.bounds.size.height + 1;
    transform = CATransform3DTranslate(transform, 0, veticalOffset, 0);
    transform = CATransform3DScale(transform, -1, -0.5, 0); // 翻转一下
    layer.instanceTransform = transform;
    //K - 0.7 = 0.3
    //透明图
    layer.instanceAlphaOffset = -0.7;
}
@end

当然还有极为重要的粒子图层

四.贝塞尔曲线API

贝塞尔曲线原理这里就不多讲了,有兴趣可以自己学习。

  • 1.初始化方法
初始化方法,需要用实例方法添加线条。 
使用比较多,可以根据需要任意定制样式,画任何我们想画的图形。
+ (instancetype)bezierPath;

返回一个矩形 path
+ (instancetype)bezierPathwithRect: (CGRect)rect;

返回一个圆形或者橢圆形 path
+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

返回一个带圆角的矩形path,矩形的四个角都是圆角
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;

返回一个带圆角的矩形path,UIRectcorner 枚举值可以设置只绘制某个圆角
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;

返回一段圆弧,参数说明:
center:弧线中心点的坐标
radius:孤线所在圆的半径 
startAngle:孤线开始的角度值 
endAngle:孤线结束的角度值 
clockwise:是否顺时针画孤线
+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius: (CGFIoat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

用一条CGPath初始化
+ (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;

返回一个反转当前路径的路径对象。(反方向绘制/path)
- (UIBezierPath*)bezierPathByReversingPath;
  • 2.属性
  • 1.CGPath:将UIBezierpath类转换成CGPath;
  • 2.currentPoint:当前path的位置,可以理解为path的终点;
  • 3.lineWidth:线条宽度;
  • 4.lineCapStyle:端点样式;
  • 5.lineJoinStyle:连接类型;
  • 6.flatness:绘线的精细程度,默认为0.6,数值越大,需要处理的时间越长;
  • 7.usesEvenOddFillRule:判断奇偶数组的规则绘制图像,图形复杂时填充颜色的一种规则(类似棋盘);
  • 8.miterLimit:最大斜接长度(只有在使用kCGLineJoinMiter时才有效,最大限制为10),边角的角度越小,斜接长度就会越大,为了避免斜接长度过长,使用lineLimit属性限制,如果斜接长度超过miterLimit,边角就会以KCALineJoinBevel类型来显示;
  • 9.- setLineDash: count: phase:为path绘制虛线,dash数组存放各段虛线的长度,count是数组元素数量,phase是起始位置;

  • lineCapStyle: 端点样式
    kCGLineCapButt 无端点
    kCGLineCapRound 圆形端点
    kCGLineCapSquare 方形端点(样式上和kCGLineCapButt是一样的,但是比它长一点)
  • lineJoinStyle: 连接类型
    kCGLineJoinMiter 尖角街接
    kCGLineJoinRound 圆角街接
    kCGLineJoinBevel 斜角街接
  • 3.UIBezierPath构建Path
以point开始作为起点,一般用“+(instancetype)bezierPath;"创建的贝塞尔曲线;
先用该方法标注一个起点,再调用其它创建线条的方法来绘制曲线。
- (void)moveToPoint:(CGPoint)point;

绘制二次贝塞尔曲线的关键方法
从path的最后一点开始添加一条线到point点
- (void)addLineToPoint:(CGPoint)point;

绘制二次贝塞尔曲线的关键方法,和- moveToPoint:‘配合使用
endpoint为终止点,controlPoint为控制点。
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;

绘制三次贝塞尔曲线的关键方法,以三个点画一段曲线。一般和moveToPoint:配合使用
起始点由”- moveToPoint :“设置
endPoint: 终止点位,controiPoint1:控制点1的坐标,controlPoint2:控制点2的坐标.
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;

绘制一段圆孤,
center: 原点坐标,radius: 半径,
startAngle: 起始角度,endAngle:终止角度,
clockwise: 是否顺时针绘制。
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

闭合线
- (void)closePath;

移除所有的点,从而有效地删除所有子路径
- (void)removeAllPoints;

追加指定的bezierPath到路径上
- (void)appendPath:(UIBezierPath *)bezierPath;

用仿射变换矩阵变换路径的所有点
- (void)applyTransform:(CGAffineTransform)transform;
  • 4.图形上下文context中的路径操作
填充路径
- (void)fill;

各个点连线
- (void)stroke;

填充模式,alpha设置
// blendMode : https://onevcat.com/2013/04/using-blending-in-ios/
- (void)fillWithBlendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha;

链接模式,alpha 设置
// blendMode : https://onevcat.com/2013/04/using-blending-in-ios/
- (void)strokeWithBlendMode: (CGBlendMode)blendMode alpha: (CGFloat)alpha;

图形绘制超出当前路径范国,则不可见
- (void)addclip;

相关文章

网友评论

      本文标题:iOS CoreAnimation(二) - CALayer/U

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