iOS中为CALayer子类的自定义属性添加动画

Core Animation 隐式的为很多图层属性添加动画,比如:position,transform,contents,通过改变其属性值,便可以达到动画效果.但如果我们要自定义CALayer.那么CALayer子类的自定义属性如何也能在创建子类后,修改属性值就可以达到动画效果呢?

先来看一下动画效果:


我们为自定义图层添加一个新属性radius,在默认情况下,radius是没有动画的,因此,改变半径会导致圆形逐渐消失并出现新的圆形.这不是我们想要的结果,我们希望在动画执行过程中,radius的动画效果与positon是同步执行的.

那么,让我们一步步实现吧!

  1. 既然是自定义图层,那么我们先创建一个类继承自CALayer,命名为CircleLayer.并且为CircleLayer添加属性radius.
@interface CircleLayer : CALayer
// 自定义属性,radius半径
@property (nonatomic,assign) CGFloat radius;

2,重写初始化方法,并且在里面调用setNeedsDisPlay:方法,这样图层的drawInContext:会在第一次添加图层的时候就会被调用.

- (instancetype)init{
    self = [super init];
    if (self) {
        [self setNeedsDisplay];
    }
    return self;
}

3,实现drawInContext:方法.在图层初始化后就会显示在这个方法里面绘制的图形.这里我们绘制的是一个圆,属性radius便是圆的半径,关于图形绘制的问题不在本次讨论之列,这里我就不在赘述了.

- (void)drawInContext:(CGContextRef)ctx{
    CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);
    CGFloat radius = self.radius;
    CGRect rect;
    rect.size = CGSizeMake(radius * 2, radius * 2);
    rect.origin.x = (self.bounds.size.width - radius * 2) / 2;
    rect.origin.y = (self.bounds.size.height - radius * 2) / 2;
    CGContextAddEllipseInRect(ctx, rect);
    CGContextFillPath(ctx);
}

4,覆盖needDisplayForKey:方法,这是最为主要的方法,在这个方法我们判断如果里面的key值与radius相同的话,便返回yes.这样我们就可以检测radius这个属性值的变化,如果发现属性值变了,就可以自动重绘.

+(BOOL)needsDisplayForKey:(NSString *)key{
    if ([key isEqualToString:@"radius"]){
        return YES;
    }
    return [super needsDisplayForKey:key];
}

5,现在要修改动作了.我们需要实现actionForKey:方法,以此返回一个在当前图层(presentationLayer)中有半径起点值的动画.这就可以让动画发生过程中,动画效果更加的平滑.

-(id<CAAction>)actionForKey:(NSString *)event{
    
    if ([self presentationLayer] != nil) {
        if ([event isEqualToString:@"radius"]) {
            CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"radius"];
            anim.fromValue = [[self presentationLayer] valueForKey:@"radius"];
            return anim;
        }
 }
    return [super actionForKey:event];
}

6,好了,基本上自定义图层的实现基本上就结束了,让我们看看如何在控制器中使用吧.在这里我用了一个动画组来展现图层的显示.

- (void)viewDidLoad {
  [super viewDidLoad];
// 初始化CircleLayer对象.
  CircleLayer *circleLayer = [CircleLayer Layer];
  circleLayer.radius =30;
 shapeLayer.backgroundColor = [UIColor blueColor].CGColor;
  circleLayer.frame = self.view.bounds;
  self.circleLayer = circleLayer;
  [self.view.layer addSublayer:circleLayer];
  // 给图层的position属性添加动画.
  CABasicAnimation *anim = [CABasicAnimation
                            animationWithKeyPath:@"position"];
  anim.duration = 2;
  NSMutableDictionary *actions = [NSMutableDictionary
                                  dictionaryWithDictionary:
                                  [circleLayer actions]];
  actions[@"position"] = anim;
  
  CABasicAnimation *fadeAnim = [CABasicAnimation 
                                animationWithKeyPath:@"opacity"];
  fadeAnim.fromValue = @0.4;
  fadeAnim.toValue = @1.0;

  CABasicAnimation *growAnim = [CABasicAnimation
                                animationWithKeyPath:
                                @"transform.scale"];
  growAnim.fromValue = @0.8;
  growAnim.toValue = @1.0;
  // 这里我们使用一个动画组,将使用baseAnimation的动画fadeAnim与growAnim添加到动画组中.用动画形式将图层展示出来.
  CAAnimationGroup *groupAnim = [CAAnimationGroup animation];
  groupAnim.animations = @[fadeAnim, growAnim];
  groupAnim.duration = 1.5;
  actions[kCAOnOrderIn] = groupAnim;
  circleLayer.actions = actions;

//给View一个手势,点击后实现自定义属性的动画.
UIGestureRecognizer *g = [[UITapGestureRecognizer alloc] 
                            initWithTarget:self
                            action:@selector(tap:)];
  [self.view addGestureRecognizer:g];
}

- (void)tap:(UIGestureRecognizer *)recognizer {
  self.circleLayer.position = CGPointMake(100, 100);
  [CATransaction setAnimationDuration:2];
// 修改radius的属性值.
  self.circleLayer.radius = 100.0;
}

需要注意的点:CALayer在运行时对这个radius自动生成了set和get方法,并且这些存取方法有重要的逻辑,关键是不要在CALayer中实现自定义的存取方法或是使用@synthesize.但是在运行过程中,会报没有实现radius属性set方法的错误.所以在这里我们使用@dynamic实现radius属性.@dynamic就是告诉编译器,不自动生成setter和getter方法,这样就可以让编译器通过编译了.

希望能对你们能有所帮助和启发.谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,551评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,141评论 5 13
  • 书写的很好,翻译的也棒!感谢译者,感谢感谢! iOS-Core-Animation-Advanced-Techni...
    钱嘘嘘阅读 2,315评论 0 6
  • 前言 本文只要描述了iOS中的Core Animation(核心动画:隐式动画、显示动画)、贝塞尔曲线、UIVie...
    GitHubPorter阅读 3,651评论 7 11
  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 3,049评论 0 21