CAMediaTiming 详解

有一种通过CAAnimation实现的协议叫做CAMediaTiming,也就是CABasicAnimation和CAKeyframeAnimation的基类(指CAAnimation)。像duration,beginTime和repeatCount这些时间相关的属性都在这个类中。大体而言,协议中定义了8个属性,这些属性通过一些方式结合在一起,准确的控制着时间。文档中每个属性只有几句话,所以很有可能在看这篇文章之前你都已经读过了,但是我觉得使用可视化的图形能更好的解释时间。

可视化的CAMediaTiming

为了显示相关属性的不同时间,无论是他们自己还是混合状态,我都会动态的将橙色变为蓝色。下面的块状显示了从开始到结束的动画过程,时间线上每一个标志代表一秒钟。你可以看到时间线上的任意一点,当前颜色即表示动画中的当前时间。比如,duration像下面一样可视。

Figure 1.设置duration为1.5秒

一旦动画完成后,CAAnimation默认从layer上移除。这从上面的图形中也表现出来了。一旦动画达到最终值,它会从layer上移除。如果layer的颜色是橙色(开始的颜色),那么颜色又会便会橙色。在这个可视化界面中,layer的颜色是白色,所以你也可以看到动画加入layer后的两秒钟又会变白,因为那是动画已经结束了。

我们也形象的描述一下动画的beginTime,这样让人更容易理解。

Figure 2.设置duration为1.5秒,开始时间为1.0秒

durations设置为1.5秒了,开始时间设为当前时间。(CACurrentMediaTime())加一秒,所以动画在2.5s后结束。动画加入到layer上之后,花费一秒钟时间来启动并呈现出来。结果是1+1.5=2.5

为了让动画从fromValue开始显示,你可以将动画设置为fill backwards。我们可以通过设置fillMode为kCAFillModeBackwards。

Figure 3.Fill mode可以让动画从fromValue开始显示

autoreverses属性可以产生从初始值到最终值,并反过来回到初始值的动画。这意味着动画发生了两次。

Figure 4.Autoreverses使得动画结束后又回到起始状态

和repeatCount比起来,repeatCount可以将动画重复两次(如下所示)或者任意次(你甚至可以使用像1.5这样的分数来完成一个半动画)。一旦动画达到它的最终值,他就会立马跳回到初始值并重新开始

Figure 5.Repeat count可以让动画运行超过一次

和repeat count类似,但很少用到的就是repeat duration了。它将会根据给定的一个duration简单的重复动画(如下2秒所示)。经过一个repeat duration时,如果它小于动画的duration,那么动画就会提前结束(repeat duration之后结束)

Figure 6.Repeat duration会让动画根据一个给定duration重复

这些都可以组合起来将一个反转动画重复多次或在一个给定的duration间重复。

Figure 7.组合

一个跟时间相关有趣的属性是speed。通过设置duration为3秒,但是speed为2,动画快速的执行了1.5秒,因为它的速度是之前的两倍。

Figure 8.速度为2时,动画执行速度是之前的两倍,所以3秒的动画只需要执行1.5秒

如果只是配置了一个简单的动画,那么你也可以分开使用beginTime和duration以达到相同的效果。但是使用speed属性的优点在于这两个事实:

1.动画的speed是分等级的。(hierarchical)

2.CAAnimation不是唯一一个实现CAMediaTiming的类。

Hierarchical speed

速度为2的动画组有一部分动画的速度为1.5,那么这个动画就是3倍于正常速度。

CAMediaTiming的其他实现

CAMediaTiming是CAAnimation实现的一个协议,但是CALayer(所有Core Animationlayers的基类)也实现了相同的协议,这就意味着你可以设置layer的speed为2.0,这样,所有加入到layer的动画运行都要快两倍。同样的,如果一个速度为3的动画加到一个速度为0.5的layer上,这个动画最终将会以1.5倍的常速运行。

为了控制动画或layer的速度,通常还可以设置speed为0,从而暂停动画。和timeOffset结合在一起时,可以通过像slider类似的控件控制动画,我们在下文中也会讲到。

刚开始timeOffset属性是非常奇怪的。正如名字所示那样,它对时间进行偏移(offset),从而计算出动画的状态。如下图所示。duration为3秒,offset为1秒的动画。

Figure 9.你可以offset整个动画,但是动画所有部分任然会执行

动画开始运行时跳过第一秒进入从橙色到蓝色的过度,直接运行剩下的两秒,直到完全变蓝。然后动画直接跳回完全橙色的时候,并完成第一秒的颜色转换。这看起来有点像我们把动画的第一秒切下来,然后放到最后。

这个属性很少用,但是它可以和一个暂停的动画(speed=0)结合在一起控制’current time’。一个暂停的动画停留在第一帧。如果你观察offset动画每次的第一个颜色,你可以看到它的颜色值一秒就进入颜色转换。将time offset设置为其他值,你可以让那段时间进入转换。

控制动画时间

同时使用speed和timeOffset可以控制动画的当前时间。这几乎不会涉及到什么代码,但是概念却比较难以理解(我希望插图能有所帮助)。为了方便,我将动画的duration设为1.0。因为time offset是绝对值。这样做就意味着当time offset为0.0时,此时就是动画的0%处(动画开始),time offset为1.0时,就是动画的100%处(动画结束)。

Slider

以一个简单的例子开始,我们为一个layer的背景色创建一个基本的动画并增加到layer上。将layer的速度设为0以暂停动画。

CABasicAnimation *changeColor =

[CABasicAnimation animationWithKeyPath:@"backgroundColor"];

changeColor.fromValue = (id)[UIColor orangeColor].CGColor;

changeColor.toValue   = (id)[UIColor blueColor].CGColor;

changeColor.duration  = 1.0; // For convenience

[self.myLayer addAnimation:changeColor

forKey:@"Change color"];

self.myLayer.speed = 0.0; // Pause the animation

当拖动slider时,在action方法中将slider的值(将slider的值设为0-1)设为layer的time offset

- (IBAction)sliderChanged:(UISlider *)sender {

self.myLayer.timeOffset = sender.value; // Update "current time"

}

下面给出了效果图:当拖动slider时动画的值便会改变,并且更新layer的背景色

Figure 10.layer的颜色随着slider的值改变而改变

拉动刷新

你还可以使用另一种机制来控制动画时间:像scroll事件。这样可以创建一个自定义下拉刷新动画,当达到加载新数据的临界值之前,用户的拖拉操作都会产生一个动画。在我的这个例子中,scroll事件控制着一个路径画笔的动画。当达到临界值时,将会启动另一种动画暗示新数据正在加载中。

这次我们使用scroll view向下拖动的总量来控制时间,为了标准化,这个值将会以points的形式,这样是非常好的,因为我们需要设置一个拖动的临界值来判断何时开始加载更多的数据。像下面那样处理代码。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

{

CGFloat offset =

scrollView.contentOffset.y+scrollView.contentInset.top;

if (offset <= 0.0 && !self.isLoading) {

CGFloat startLoadingThreshold = 60.0;

CGFloat fractionDragged       = -offset/startLoadingThreshold;

self.pullToRefreshShape.timeOffset = MAX(0.0, fractionDragged);

if (fractionDragged >= 1.0) {

[self startLoading];

}

}

}

像这样控制动画:

CABasicAnimation *writeText =

[CABasicAnimation animationWithKeyPath:@"strokeEnd"];

writeText.fromValue = @0;

writeText.toValue = @1;

CABasicAnimation *move =

[CABasicAnimation animationWithKeyPath:@"position.y"];

move.byValue = @(-22);

move.toValue = @0;

CAAnimationGroup *group = [CAAnimationGroup animation];

group.duration = 1.0;

group.animations = @[writeText, move];

结果是:下拉视图时,可以直接控制动画的进度。如果你抬起手动画将会退回去。

Figure 11.使用scroll事件直接控制拖动刷新

self.isLoading = YES;

// start the loading animation

[self.loadingShape addAnimation:[self loadingAnimation]

forKey:@"Write that word"];

CGFloat contentInset    = self.collectionView.contentInset.top;

CGFloat indicatorHeight = CGRectGetHeight(self.loadingIndicator.frame);

// inset the top to keep the loading indicator on screen

self.collectionView.contentInset =

UIEdgeInsetsMake(contentInset+indicatorHeight, 0, 0, 0);

self.collectionView.scrollEnabled = NO; // no further scrolling

[self loadMoreDataWithAnimation:^{

// during the reload animation (where new cells are inserted)

self.collectionView.contentInset =

UIEdgeInsetsMake(contentInset, 0, 0, 0);

self.loadingIndicator.alpha = 0.0;

} completion:^{

// reset everything

[self.loadingShape removeAllAnimations];

self.loadingIndicator.alpha = 1.0;

self.collectionView.scrollEnabled = YES;

self.pullToRefreshShape.timeOffset = 0.0; // back to the start

self.isLoading = NO;

}];

最终,当你向下拖动超过临界值时便会像这样

Figure 12.拖动刷新和加载动画
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容