Core Animation:动画和CALayer子类(代码示例和图示)

Core Animation

1、Core Animation是图形渲染和动画的基础设施。
2、Core Animation使用GPU绘图,而UIView调用drawRect重绘时是用CPU,所以Core Animation的绘图性能更优。Core Animation先对CALayer对象生成一个位图,然后需要绘制的时候将这个位图和它的状态信息比如位置和变形传给GPU进行绘制。
3、CALayer的position指的是其anchorPoint所在点相对于父视图的坐标,比如anchorPoint(0.5,0.5)是layer的中心点,position就是中心点相对于父视图的位置,而UIView的frame.origin指的是左上角相对于父视图的位置。
4、创建一个CALayer的花销要比创建一个UIView小,所以如果只是为了添加一个显示的内容而不需要UIView提供的其他特性比如事件处理,则直接用CALayer,也就是这个layer没有对应的UIView对象。
5、不同类型的layer有不同的用法,比如CAEmitterLayer控制粒子的生成和位置,CAGradientLayer用来绘画渐变色。也许layer其实封装了不同的绘图渲染代码。
6、如果layer的背景颜色不透明,则将opaque属性设为YES可以提高混合屏幕上的layer时的性能。将opaque设为YES让Core Animation知道它不需要为那个layer维护一个alpha通道,但如果直接给layer的contents属性设置图片则那个图片的alpha通道会保留。当layer的cornerRadius非0时不能设为YES。
7、cornerRadius会影响layer的背景颜色和border,设置masksToBounds为YES时才会影响layer的contents属性中的图片。
8、给layer显式地添加动画比如CABasicAnimation+opacity,在动画结束后不会修改layer的opacity属性,所以如果想修改layer的opacity必须在添加动画后设置为新值。
9、当给layer添加了多个动画,则默认情况下这些动画同时开始(除非修改了动画timing,比如beginTime属性设置延迟几秒),因为显式动画、隐式动画都是在当前run loop cycle结束后开始执行的。
10、如果一个layer属于一个UIView,则创建动画时推荐使用UIKit提供的动画接口而不是Core Animation的接口。如果想使用Core Animation的接口给UIView的layer添加动画,必须在UIKit动画接口的block中调用Core Animation动画相关代码,因为UIView会默认关闭layer动画但是会在block中重新打开。
11、由于对UIView的layer的修改会自动反映在UIView对象中,所以UIView、Core Animation的接口都可以用来做修改。
12、CATransaction的begin、commit类方法之间的代码就是一个事务,中间代码可以修改隐式动画的默认动画参数比如时长。CATransaction可以嵌套,内层和外层事务可以设置不同的动画参数。
13、性能优化:(1)一般来说,多个简单路径CAShapeLayer和单一复杂路径的CAShapeLayer,前者性能更高,具体还是要用Instruments比较两者的性能差异。(2)如果同一个图片用于多个layer,则应该显式地设置contents属性。(3)layer的高宽用整数。(4)设置CALayer的shadowPath属性来指定阴影的形状。但是,如果没有指定自定义的阴影形状,则阴影是基于layer的非全透明部分,也就是包括内容、边框、sublayer,比如如果本图层的内容contents是一个带透明通道的图片则阴影轮廓是不规则图片的边缘。
14、mask用来遮盖layer的全部或部分内容。
15、CALayer大部分属性都有隐式动画,比如sublayers、shadowPath、masksToBounds等,frame没有动画。

CALayer子类示例

CALayer和CAShapeLayer

1、position指的是子图层的anchorPoint相对于父图层的位置,子图层的anchorPoint在中心点,由于anchorPoint的取值在0-1所以是{0.5, 0.5}。
2、阴影:
(1)shadowPath设置阴影的形状。
(2)没有设置shadowPath时,阴影会应用到子图层,本图层和子图层的不透明边框、带有alpha通道的图片不透明部分有阴影;backgroundColor会遮盖阴影。
3、代码示例:

	CALayer *layer = [[CALayer alloc] init];
    layer.frame = CGRectMake(20, 50, 200, 150);
    layer.borderColor = [UIColor greenColor].CGColor;
    layer.borderWidth = 5;
    layer.shadowColor = [UIColor redColor].CGColor;
    layer.shadowOffset = CGSizeMake(15, 15);
    layer.shadowOpacity = 1;
//    layer.shadowPath = CGPathCreateWithRoundedRect(layer.bounds, 50, 50, nil); // 设置阴影的形状,所以阴影的形状不再是本图层和子图层的不透明部分的形状
    layer.contents = (id)[UIImage imageNamed:@"butterfly"].CGImage;
    layer.contentsGravity = kCAGravityResizeAspect; // 对内容图片进行调整
    [self.view.layer addSublayer:layer];
    
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.bounds = CGRectMake(0, 0, 30, 100);
    shapeLayer.position = CGPointMake(CGRectGetWidth(shapeLayer.bounds)*0.5, 50);
    shapeLayer.borderColor = [UIColor purpleColor].CGColor;
    shapeLayer.borderWidth = 2;
    shapeLayer.path = CGPathCreateWithEllipseInRect(CGRectMake(5, 20, 20, 60), nil);
    [layer addSublayer:shapeLayer];

效果图:
在这里插入图片描述

CATextLayer

1、默认显示的文字有点模糊,需要修改contentsScale为[UIScreen mainScreen].scale。
2、代码示例:

		CATextLayer *layer = [CATextLayer layer];
        layer.string = @"CATextLayer";
        layer.backgroundColor = [UIColor blueColor].CGColor;
        layer.fontSize = 17;
        layer.frame = CGRectMake(20, 150, 100, 50);
        layer.contentsScale = [UIScreen mainScreen].scale;
        [self.view.layer addSublayer:layer];

左边是设置contentsScale之前,右边是设置之后。
在这里插入图片描述

CAGradientLayer

	CAGradientLayer *layer = [CAGradientLayer layer];
    layer.frame = CGRectMake(20, 50, 320, 50);
    layer.colors = @[(id)[UIColor redColor].CGColor, (id)[UIColor greenColor].CGColor, (id)[UIColor blueColor].CGColor];
    layer.locations = @[@0.2, @0.5, @0.6]; // 表示0.2的位置是红色,0.5的位置是绿色,0-0.2之间为红色,0.2-0.5之间从红色过渡到绿色,未修改startPoint、endPoint时看起来比较明显
    layer.startPoint = CGPointMake(1, 1); // 默认是{0.5, 0},表示上边界中间,是颜色梯度的起始点,也就是红色的位置
    layer.endPoint = CGPointMake(0.2, 0.3);
//    layer.type = kCAGradientLayerAxial; // kCAGradientLayerAxial颜色交接处为直线;kCAGradientLayerRadial颜色交接处为弧形
    [self.view.layer addSublayer:layer];

在这里插入图片描述

CAEmitterLayer

	CAEmitterCell *cell = [CAEmitterCell emitterCell];
    cell.birthRate = 50; // 每秒发射多少个
    cell.lifetime = 10;
    cell.velocity = 100; // 负数则方向向下
    cell.scale = 0.2; // 对原图片的缩放
    cell.emissionRange = M_PI_4 * 3; // 发射范围
    cell.contents = (__bridge id _Nullable)([UIImage imageNamed:@"petal"].CGImage);
    cell.color = [UIColor systemPinkColor].CGColor; // 修改图片颜色
    cell.blueRange = 0.9;
    cell.spin = M_PI / 6.0; // 设置图片旋转,每秒多少弧度
    
    CAEmitterLayer *layer = [CAEmitterLayer layer];
    layer.emitterPosition = CGPointMake(150, 150); // 发射位置
    layer.emitterCells = @[cell];
    [self.view.layer addSublayer:layer];

在这里插入图片描述
设置cell.velocity = -100; cell.emissionRange = M_PI_4 * 2; layer.emitterSize = CGSizeMake(600, 1); layer.emitterShape = kCAEmitterLayerLine;可实现粒子从一条水平线往下飘落。
emitterSize设置发射器的shape的大小;emitterShape发射器形状,默认是point。

CAScrollLayer

1、默认滚动方向scrollMode,水平、竖直都可以。
2、
(1)scrollToPoint实现滚动功能,且必须scrollMode支持这个滚动方向才会生效。
(2)point.x为正数则将sublayer在原frame的基础上向左平移;point.x为负数则sublayer向右平移;point.y为正数则向上;point.y为负数则向下。
3、CAScrollLayer创建后,并不响应任何手势。(而UIScrollView设置了合适的contentSize之后就可以响应手势滚动。)
4、代码示例:

	CAGradientLayer *sublayer = [CAGradientLayer new];
    sublayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor greenColor].CGColor];
    sublayer.startPoint = CGPointMake(0, 0);
    sublayer.endPoint = CGPointMake(1, 1);
    sublayer.frame = CGRectMake(50, 50, 500, 600);
    
    CAScrollLayer *layer = [CAScrollLayer layer];
    layer.frame = CGRectMake(20, 50, 200, 150);
    layer.name = TempLayerName;
    layer.backgroundColor = [UIColor blueColor].CGColor;
    [layer addSublayer:sublayer];
//    [layer scrollToPoint:CGPointMake(300, 300)];
    [self.view.layer addSublayer:layer];

CATiledLayer

	CATiledLayer *layer = [CATiledLayer layer];
    layer.name = TempLayerName;
    layer.backgroundColor = [UIColor blueColor].CGColor;
//    layer.contents = (__bridge id)[UIImage imageNamed:@"butterfly.jpeg"].CGImage; // CATiledLayer不能直接设置contents,而应该在drawLayer:inContext:中添加,这个方法会在填充tile时多次调用
    layer.delegate = self;
    layer.frame = CGRectMake(20, 50, 300, 150);
    layer.tileSize = CGSizeMake(20, 20);
    [self.view.layer addSublayer:layer];

/** CALayerDelegate的方法 */
- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)ctx {
   
    CGRect bounds = CGContextGetClipBoundingBox(ctx);
    UIGraphicsPushContext(ctx);
    UIImage *tileImage = nil;
    if (arc4random_uniform(2) == 0) {
   
        tileImage = [UIImage imageNamed:@"mountain"];
    } else {
   
        tileImage = [UIImage imageNamed:@"petal"];
    }
    [tileImage drawInRect:bounds];
    UIGraphicsPopContext();
}

CATiledLayer应该每个tile显示一张大图的相应部分,下面的例子只是为了显示每个tile会有逐渐显示的效果,也就是+fadeDuration指示的0.25s。
在这里插入图片描述

CAReplicatorLayer

	CALayer *sublayer = [CALayer layer];
    sublayer.frame = CGRectMake(0, 0, 50, 50);
    sublayer.cornerRadius = CGRectGetWidth(sublayer.frame) * 0.5;
    sublayer.backgroundColor = [UIColor redColor].CGColor;
    sublayer.borderWidth = 1;
    
    CAReplicatorLayer *layer = [CAReplicatorLayer layer];
    layer.name = TempLayerName;
    layer.frame = CGRectMake(20, 50, 300, 150);
    [layer addSublayer:sublayer];
    layer.instanceCount = 5;
    layer.instanceTransform = CATransform3DMakeTranslation(20, 0, 0); // 第k个实例相对于第k-1的偏移,不设置时是重合的
//    layer.instanceDelay = 2; // 给sublayer加动画时才生效
    [self.view.layer addSublayer:layer];

在这里插入图片描述
CAReplicatorLayer的动画示例在“动画-CAAnimationGroup”。

动画

CABasicAnimation

1、可以动画的属性在Core Animation Programming Guide - Appendix B: Animatable Properties中列出(查看CALayer、CAShapeLayer官方文档也可以知道哪些属性是可动画的,如下图),+ animationWithKeyPath:的keyPath可以填入这些属性名(比如CAShapeLayer的strokeStart)。另外,比如“transform.scale”也可以作为keyPath,详情参看Core Animation Programming Guide - Appendix C: Key-Value Coding Extensions。(mask是一个CALayer,不可以给CALayer的mask属性加动画,但是可以给mask这个CALayer加动画。)
在这里插入图片描述
2、代码示例:

// 按钮1的action
- (void)action1 {
   
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.fromValue = @1;
    animation.toValue = @0;
    animation.duration = 1;
    animation.repeatCount = 3;
    animation.removedOnCompletion = YES; // YES表示动画结束之后移除,移除之后使用animationForKey获取不到这个动画;但是,使用dispatch_after会获取到,点击其他按钮调用animationForKey获取不到,也许是因为要等到下一个runloop才移除动画?
    [self.view.layer addAnimation:animation forKey:@"opacityAnimationKey"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
   
        NSLog(@"%@", [self.view.layer animationForKey:@"opacityAnimationKey"]);
    });
    dispatch_after(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值