我们都知道,CALayer和CAAnimation都实现了CAMediaTiming 协议,因此在Core Animation中,理解CAMediaTiming协议中的属性是非常必要的,但是苹果的文档中对于各个属性描述太简单,对初学者容易理解,这篇文章主要帮助理解CAMediaTiming协议中各个属性的含义。
CAMediaTiming Protocol提供了8个属性,下面将分别讲解。
duration
Specifies the basic duration of the animation, in seconds.
上面是官方文档的解释,这个属性一目了然,不需要做过多的解释,唯一有一点需要注意的是我们设置的duration可能和动画进行的真实duration不一样,这个依赖与superlayer的time space 或者就是speed。
speed
Specifies how time is mapped to receiver’s time space from the parent time space. (required)
其实也比较好理解,如果一个动画A :duration为1秒,speed为1;而另一个动画B:duration为2秒,speed为2。则两个动画的效果是相同的。不过前提是它们的super layer相同。
属性repeatCount , repeatDuration, autoreverses很容易理解,再此不在讲述。下面主要讨论剩下的三个属性
beginTime
Specifies the begin time of the receiver in relation to its parent object, if applicable.
如果一个animation是在一个animation group中,则beginTime就是其parent object——animation group 开始的一个偏移。如果一个animation 的 beginTime为5,则此动画在group aniamtion开始之后的5s在开始动画。
如果一个animation是直接添加在layer上,beginTime同样是是其parent object——layer 开始的一个偏移,但是一个layer的beginning是一个过去的时间(猜想layer的beginning可能是其被添加到layer tree上的时间),因此不能简单的设置beginTime为5去延迟动画5s之后开始,因为有可能layer的beginning加上5s之后也是一个过去的时间(很有可能),因此,当要延迟一个添加到layer上的动画的时候,需要定义一个addTime,因此
animation.beginTime = addTime + delay;
通过使用CACurrentMediaTime去获取addTime:
addTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
如果一个layer他自己的beginTime已经设置,则animation的addTime的计算必须在layer的beginTime设置之后,因为要有一个时间的转移,具体看下面的例子:
CFTimeInterval currentTime = CACurrentMediaTime();
CFTimeInterval currentTimeInSuperLayer = [superLayer convertTime:currentTime fromLayer:nil];
layer.beginTime = currentTimeInSuperLayer + 2;
CFTimeInterval currentTimeInLayer = [layer convertTime:currentTimeInSuperLayer fromLayer:superLayer];
CFTimeInterval addTime = currentTimeInLayer;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.beginTime = addTime + 1;
group.animations = [NSArray arrayWithObject:anim];
group.duration = 2;
anim.beginTime = 0.5;
[layer addAnimation:group forKey:nil];
timeOffset
Specifies an additional time offset in active local time. (required)
此句话不太容易理解,但是通过一个例子很容易理解。
假定一个3s的动画,它的状态为t0,t1,t2,t3,当没有timeOffset的时候,正常的状态序列应该为:
t0->t1->t2->t3
当设置timeOffset为1的时候状态序列就变为
t1->t2->t3->t0
同理当timeOffset为2的时候状态序列就变为:
t2->t3->t0=>t2
是不是理解了?
下面举一个例子,效果图参见空间图片地址
http://my.csdn.net/my/album/detail/1651105 (不知道怎么直接嵌入的,因为是个gif,直接上传也不会播放,哪位同学知道好的处理方法可以告诉我)
主要代码如下:
// Add layer.
CATextLayer *A = [CATextLayer layer];
A.string = @"A";
A.fontSize = 48;
A.foregroundColor = [UIColor blackColor].CGColor;
A.bounds = CGRectMake(0, 0, 48, 48);
A.position = self.center;
[self.layer addSublayer:A];
// Move animation.
CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:@"position"];
move.values = [NSArray arrayWithObjects:[NSValue valueWithCGPoint:CGPointMake(CGRectGetMinX(self.bounds), CGRectGetMaxY(self.bounds))], [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMinY(self.bounds))], [NSValue valueWithCGPoint:CGPointMake(CGRectGetMaxX(self.bounds), CGRectGetMaxY(self.bounds))], nil];
move.calculationMode = kCAAnimationCubic;
move.duration = 10;
move.speed = 2;
// Opacity animation.
CABasicAnimation *opacity = [CABasicAnimation animationWithKeyPath:@"opacity"];
opacity.toValue = [NSNumber numberWithFloat:0];
opacity.duration = 2.5;
opacity.beginTime = 2.5; // Fade from the half way.
// Animatin group.
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:move, opacity, nil];
group.duration = 8;
group.repeatCount = HUGE_VALF;
// Time warp.
CFTimeInterval currentTime = CACurrentMediaTime();
CFTimeInterval currentTimeInSuperLayer = [self.layer convertTime:currentTime fromLayer:nil];
A.beginTime = currentTimeInSuperLayer + 5; // Delay the appearance of A.
CFTimeInterval currentTimeInLayer = [A convertTime:currentTimeInSuperLayer fromLayer:self.layer];
CFTimeInterval addTime = currentTimeInLayer;
group.beginTime = addTime + 3; // Delay the animatin group.
[A addAnimation:group forKey:nil];
// Timer. For nice visual effect. Optional.
CATextLayer *timer = [CATextLayer layer];
timer.fontSize = 48;
timer.foregroundColor = [UIColor redColor].CGColor;
timer.bounds = CGRectMake(0, 0, 48, 48);
timer.position = self.center;
[self.layer addSublayer:timer];
CAKeyframeAnimation *count = [CAKeyframeAnimation animationWithKeyPath:@"string"];
count.values = [NSArray arrayWithObjects:@"5", @"4", @"3", @"2", @"1", nil];
CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
fade.toValue = [NSNumber numberWithFloat:0.2];
group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:count, fade, nil];
group.duration = 5;
[timer addAnimation:group forKey:nil];
大概解释一下,其实就是先播放一个倒计时5-4-3-2-1的动画,之后在显示layer A,因为A的beginTime为5,而当A显示3秒之后,A上才开始做group的动画。
fillMode
文档中关于fillMode的讲解也不是很清楚,fillMode有4中类型
kCAFillModeRemoved (default)
kCAFillModeForwards
kCAFillModeBackwards
kCAFillModeBoth
用语言去解释这些属性,会让人难以理解,所以这里用例子去解释,看下面代码:
CALayer *colorLayer = [[ CALayer alloc] init];
colorLayer.position = self.center;
colorLayer.backgroundColor = [UIColor redColor].CGColor;
colorLayer.bounds = CGRectMake(0.0f, 0.0f, 20.0f, 20.0f);
[self.layer addSublayer:colorLayer];
CABasicAnimation *boundAn = [CABasicAnimation animationWithKeyPath:@"bounds"];
boundAn.fromValue = [NSValue valueWithCGRect:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
boundAn.toValue = [NSValue valueWithCGRect:CGRectMake(0.0f, 0.0f, 400.0f, 400.0f)];
boundAn.beginTime = 2.0f;
boundAn.duration = 5.0f;
boundAn.fillMode = kCAFillModeRemoved;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObject:boundAn];
group.duration = 10.0f;
[colorLayer addAnimation:group forKey:nil];
kCAFillModeRemoved也是fillMode的默认属性,此时动画的效果:开始时,colorLayer的size为(20,20),当到2s的时候,colorLayer的size突然就变为(100,100),然后开始做动画,当到7s的时候colorLayer的duration已经完成,此时colorLayer的size会突然 变为(20,20),然后在持续3s,当总时间到10s时结束。
当fillMode的属性设置为kCAFillModeForwards的时候,动画效果为:开始时,colorLayer的size为(20,20),当到2s的时候,colorLayer的size突然就变为(100,100),然后开始做动画,之前都和kCAFillModeRemoved都一样,不一样的时当到7s的时候colorLayer的duration已经完成,此时colorLayer的size还会保持在(400,400),然后在持续3s,当总时间到10s时结束,此时size才变为(20,20)
当fillMode的属性设置为kCAFillModeBackwards的时候,动画效果为:开始时,colorLayer的size就为(100,100),当到2s的时候,colorLayer开始做动画,当到7s的时候colorLayer的duration已经完成,此时colorLayer的size会突然 变为(20,20),然后在持续3s,当总时间到10s时结束。
kCAFillModeBoth就不在解释了,猜都能猜到应该是kCAFillModeForwards和kCAFillModeBackwards的结合。
至此,应该对CAMediaTiming 协议中的属性应该有一定了解了。
本文参考了苹果官方文档
CAMediaTiming Protocol Preference 以及博文
time warp in animation