CABasicAnimation算是CAKeyFrameAnimation的 特殊情況,即不考慮中間變換過程,只考慮起始點與目標點就可以了。而CAKeyFrameAnimation則更復雜一些,允許我們在起點與終點間自定義 更多內容來達到我們的實際應用需求!比如,手機淘寶中,當你添加物品到購物車后會出現將物品拋到購物車的效果,這種效果實現起來也不難,無非是先繪制拋物 線在執行position以及scale的GroupAnimation而已,以下圖1是我模仿該功能小玩出來的一個demo示例,感興趣的話你可以自己 實現一下試試:D.

圖1 圖2
下面我們以實現“小圓球繞矩形跑道循環跑動”為目標開始對CAKeyFrameAnimation的介紹,如圖2所示。小圓球的運動軌跡可分為四段,每段的運動速度不同,第一段中先慢后快再慢。先貼上源碼方便后面分析:
1 //繞矩形循環跑 2 - (void)initRectLayer 3 { 4 rectLayer = [[CALayer alloc] init]; 5 rectLayer.frame = CGRectMake(15, 200, 30, 30); 6 rectLayer.cornerRadius = 15; 7 rectLayer.backgroundColor = [[UIColor blackColor] CGColor]; 8 [self.view.layer addSublayer:rectLayer]; 9 CAKeyframeAnimation *rectRunAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 10 //設定關鍵幀位置,必須含起始與終止位置 11 rectRunAnimation.values = @[[NSValue valueWithCGPoint:rectLayer.frame.origin], 12 [NSValue valueWithCGPoint:CGPointMake(320 - 15, 13 rectLayer.frame.origin.y)], 14 [NSValue valueWithCGPoint:CGPointMake(320 - 15, 15 rectLayer.frame.origin.y + 100)], 16 [NSValue valueWithCGPoint:CGPointMake(15, rectLayer.frame.origin.y + 100)], 17 [NSValue valueWithCGPoint:rectLayer.frame.origin]]; 18 //設定每個關鍵幀的時長,如果沒有顯式地設置,則默認每個幀的時間=總duration/(values.count - 1) 19 rectRunAnimation.keyTimes = @[[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:0.6], 20 [NSNumber numberWithFloat:0.7], [NSNumber numberWithFloat:0.8], 21 [NSNumber numberWithFloat:1]]; 22 rectRunAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 23 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], 24 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear], 25 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]]; 26 rectRunAnimation.repeatCount = 1000; 27 rectRunAnimation.autoreverses = NO; 28 rectRunAnimation.calculationMode = kCAAnimationLinear; 29 rectRunAnimation.duration = 4; 30 [rectLayer addAnimation:rectRunAnimation forKey:@"rectRunAnimation"]; 31 }

圖3
對 CAKeyFrameAnimation的使用與CABasicAnimation大同小異,有些屬性是共通的,因此小翁建議你先閱讀上一篇文章。 KeyFrame的意思是關鍵幀,所謂“關鍵”就是改變物體運動趨勢的幀,在該點處物體將發生運動狀態,比如矩形的四個角,拋物線的頂點等。因此,聰明的 你應該知道了,在上述例子中共有5個關鍵幀(圖3中的ABCDE)。上個關鍵幀到當前關鍵幀之間的路徑與當前關鍵幀相聯系,比如AB->B,我們可 以對AB進行定義動畫定義,而自定義要通過眾多CAKeyFrameAnimation的屬性達到目的。CAKeyFrameAnimation的使用中 有以下主要的屬性需要注意,有些屬性可能比較繞比較難以理解,我會結合圖片進行必要的說明。
(1)values屬性
values屬性指明整個動畫過程中的關鍵幀點,例如上例中的A-E就是通過values指定的。需要注意的是,起點必須作為values的第一個值。
(2)path屬性
作 用與values屬性一樣,同樣是用於指定整個動畫所經過的路徑的。需要注意的是,values與path是互斥的,當values與path同時指定 時,path會覆蓋values,即values屬性將被忽略。例如上述例子等價於代碼中values方式的path設置方式為:
1 CGMutablePathRef path = CGPathCreateMutable(); 2 CGPathMoveToPoint(path, NULL, rectLayer.position.x - 15, rectLayer.position.y - 15); 3 CGPathAddLineToPoint(path, NULL, 320 - 15, rectLayer.frame.origin.y); 4 CGPathAddLineToPoint(path, NULL, 320 - 15, rectLayer.frame.origin.y + 100); 5 CGPathAddLineToPoint(path, NULL, 15, rectLayer.frame.origin.y + 100); 6 CGPathAddLineToPoint(path, NULL, 15, rectLayer.frame.origin.y); 7 rectRunAnimation.path = path; 8 CGPathRelease(path);
(3)keyTimes屬性
該 屬性是一個數組,用以指定每個子路徑(AB,BC,CD)的時間。如果你沒有顯式地對keyTimes進行設置,則系統會默認每條子路徑的時間 為:ti=duration/(5-1),即每條子路徑的duration相等,都為duration的1\4。當然,我們也可以傳個數組讓物體快慢結 合。例如,你可以傳入{0.0, 0.1,0.6,0.7,1.0},其中首尾必須分別是0和1,因此tAB=0.1-0, tCB=0.6-0.1, tDC=0.7-0.6, tED=1-0.7.....
(4)timeFunctions屬性
用過UIKit層動畫的同學應該對這個屬性不陌生,這個屬性用以指定時間函數,類似於運動的加速度,有以下幾種類型。上例子的AB段就是用了淡入淡出效果。記住,這是一個數組,你有幾個子路徑就應該傳入幾個元素
1 kCAMediaTimingFunctionLinear//線性 2 kCAMediaTimingFunctionEaseIn//淡入 3 kCAMediaTimingFunctionEaseOut//淡出 4 kCAMediaTimingFunctionEaseInEaseOut//淡入淡出 5 kCAMediaTimingFunctionDefault//默認
(5)calculationMode屬性
該屬性決定了物體在每個子路徑下是跳着走還是勻速走,跟timeFunctions屬性有點類似
1 const kCAAnimationLinear//線性,默認 2 const kCAAnimationDiscrete//離散,無中間過程,但keyTimes設置的時間依舊生效,物體跳躍地出現在各個關鍵幀上 3 const kCAAnimationPaced//平均,keyTimes跟timeFunctions失效 4 const kCAAnimationCubic//平均,同上 5 const kCAAnimationCubicPaced//平均,同上
此外,動畫的暫停與開始可以通過下面的方式做到:
1 -(void)pauseLayer:(CALayer*)layer 2 { 3 CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; 4 layer.speed = 0.0; 5 layer.timeOffset = pausedTime; 6 } 7 8 -(void)resumeLayer:(CALayer*)layer 9 { 10 CFTimeInterval pausedTime = [layer timeOffset]; 11 layer.speed = 1.0; 12 layer.timeOffset = 0.0; 13 layer.beginTime = 0.0; 14 CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; 15 layer.beginTime = timeSincePause; 16 }
