CABasicAnimation使用總結
實例化
使用方法animationWithKeyPath:
對 CABasicAnimation進行實例化,並指定Layer的屬性作為關鍵路徑進行注冊。
//圍繞y軸旋轉 CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
設定動畫
設定動畫的屬性和說明
屬性 | 說明 |
---|---|
duration | 動畫的時長 |
repeatCount | 重復的次數。不停重復設置為 HUGE_VALF |
repeatDuration | 設置動畫的時間。在該時間內動畫一直執行,不計次數。 |
beginTime | 指定動畫開始的時間。從開始延遲幾秒的話,設置為【CACurrentMediaTime() + 秒數】 的方式 |
timingFunction | 設置動畫的速度變化 |
autoreverses | 動畫結束時是否執行逆動畫 |
fromValue | 所改變屬性的起始值 |
toValue | 所改變屬性的結束時的值 |
byValue | 所改變屬性相同起始值的改變量 |
transformAnima.fromValue = @(M_PI_2); transformAnima.toValue = @(M_PI); transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transformAnima.autoreverses = YES; transformAnima.repeatCount = HUGE_VALF; transformAnima.beginTime = CACurrentMediaTime() + 2;
防止動畫結束后回到初始狀態
只需設置removedOnCompletion
、fillMode
兩個屬性就可以了。
transformAnima.removedOnCompletion = NO; transformAnima.fillMode = kCAFillModeForwards;
解釋:為什么動畫結束后返回原狀態?
首先我們需要搞明白一點的是,layer動畫運行的過程是怎樣的?其實在我們給一個視圖添加layer動畫時,真正移動並不是我們的視圖本身,而是 presentation layer 的一個緩存。動畫開始時 presentation layer開始移動,原始layer隱藏,動畫結束時,presentation layer從屏幕上移除,原始layer顯示。這就解釋了為什么我們的視圖在動畫結束后又回到了原來的狀態,因為它根本就沒動過。
這個同樣也可以解釋為什么在動畫移動過程中,我們為何不能對其進行任何操作。
所以在我們完成layer動畫之后,最好將我們的layer屬性設置為我們最終狀態的屬性,然后將presentation layer 移除掉。
添加動畫
[self.imageView.layer addAnimation:transformAnima forKey:@"A"];
需要注意的兩點
- 一個 CABasicAniamtion 的實例對象只是一個數據模型,和他綁定到哪一個layer上是沒有關系的
- 方法
addAnimation:forKey:
是將 CABasicAniamtion 對象進行了 copy 操作的。所以在將其添加到一個layer上之后,我們還是將其再次添加到另一個layer上的。
fillMode屬性的理解
該屬性定義了你的動畫在開始和結束時的動作。默認值是 kCAFillModeRemoved
。
取值的解釋
- kCAFillModeRemoved 設置為該值,動畫將在設置的 beginTime 開始執行(如沒有設置beginTime屬性,則動畫立即執行),動畫執行完成后將會layer的改變恢復原狀。

- kCAFillModeForwards 設置為該值,動畫即使之后layer的狀態將保持在動畫的最后一幀,而removedOnCompletion的默認屬性值是 YES,所以為了使動畫結束之后layer保持結束狀態,應將removedOnCompletion設置為NO。

- kCAFillModeBackwards 設置為該值,將會立即執行動畫的第一幀,不論是否設置了 beginTime屬性。觀察發現,設置該值,剛開始視圖不見,還不知道應用在哪里。

- kCAFillModeBoth 該值是 kCAFillModeForwards 和 kCAFillModeBackwards的組合狀態

Animation Easing的使用
也即是屬性timingFunction
值的設定,有種方式來獲取屬性值
(1)使用方法functionWithName:
這種方式很簡單,這里只是簡單說明一下取值的含義:
-
kCAMediaTimingFunctionLinear 傳這個值,在整個動畫時間內動畫都是以一個相同的速度來改變。也就是勻速運動。
-
kCAMediaTimingFunctionEaseIn 使用該值,動畫開始時會較慢,之后動畫會加速。
- kCAMediaTimingFunctionEaseOut 使用該值,動畫在開始時會較快,之后動畫速度減慢。
- kCAMediaTimingFunctionEaseInEaseOut 使用該值,動畫在開始和結束時速度較慢,中間時間段內速度較快。
(2)使用方法functionWithControlPoints: : : :
實現,這個之后再說,占個坑先。
其他的一些設置屬性
- repeatCount 設置動畫的執行次數
- autoreverses 默認值為 NO,將其設置為 YES
- speed 改變動畫的速度 可以直接設置動畫上的speed屬性,這樣只有這個動畫速度。
或者在layer上設置speed屬性,這樣在該視圖上的所有動畫都提速,該視圖上的所有子視圖上的動畫也會提速。animation.speed = 2;
speed兩點需注意的:
(1) 如果設置動畫時間為4s,speed設置為2,則動畫只需2s即可執行完。
(2)如果同時設置了動畫的speed和layer 的speed,則實際的speed為兩者相乘。
使用總結
- 在動畫執行完成之后,最好還是將動畫移除掉。也就是盡量不要設置
removedOnCompletion
屬性為NO fillMode
盡量取默認值就好了,不要去設置它的值。只有在極個別的情況下我們會修改它的值,以后會說到,這里先占個坑。- 解決有時視圖會閃動一下的問題,我們可以將layer的屬性值設置為我們的動畫最后要達到的值,然后再給我們的視圖添加layer動畫。
例子(移動動畫實現)
直接上代碼
CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"]; positionAnima.duration = 0.8; positionAnima.fromValue = @(self.imageView.center.y); positionAnima.toValue = @(self.imageView.center.y-30); positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; positionAnima.repeatCount = HUGE_VALF; positionAnima.repeatDuration = 2; positionAnima.removedOnCompletion = NO; positionAnima.fillMode = kCAFillModeForwards; [self.imageView.layer addAnimation:positionAnima forKey:@"AnimationMoveY"];
組合動畫的實現
CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"]; positionAnima.fromValue = @(self.imageView.center.y); positionAnima.toValue = @(self.imageView.center.y-30); positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; transformAnima.fromValue = @(0); transformAnima.toValue = @(M_PI); transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; CAAnimationGroup *animaGroup = [CAAnimationGroup animation]; animaGroup.duration = 2.0f; animaGroup.fillMode = kCAFillModeForwards; animaGroup.removedOnCompletion = NO; animaGroup.animations = @[positionAnima,transformAnima]; [self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];
動畫開始和結束時的事件
為了獲取動畫的開始和結束事件,需要實現協議
positionAnima.delegate = self;
代理方法實現
//動畫開始時 - (void)animationDidStart:(CAAnimation *)anim { NSLog(@"開始了"); } //動畫結束時 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { //方法中的flag參數表明了動畫是自然結束還是被打斷,比如調用了removeAnimationForKey:方法或removeAnimationForKey方法,flag為NO,如果是正常結束,flag為YES。 NSLog(@"結束了"); }
其實比較重要的是有多個動畫的時候如何在代理方法中區分不同的動畫
兩種方式
方式一:
如果我們添加動畫的視圖是全局變量,可使用該方法。
添加動畫時,我們使用了
[self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];
所以,可根據key來區分不同的動畫
//動畫開始時 - (void)animationDidStart:(CAAnimation *)anim { if ([anim isEqual:[self.imageView.layer animationForKey:@"Animation"]]) { NSLog(@"動畫組執行了"); } }
Note:把動畫存儲為一個屬性然后再回調中比較,用來判定是哪個動畫是不可行的。應為委托傳入的動畫參數是原始值的一個深拷貝,不是同一個值
方式二
添加動畫的視圖是局部變量時,可使用該方法
添加動畫給動畫設置key-value對
[positionAnima setValue:@"PositionAnima" forKey:@"AnimationKey"]; [transformAnima setValue:@"TransformAnima" forKey:@"AnimationKey"];
所以,可以根據key中不同的值來進行區分不同的動畫
//動畫結束時 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"PositionAnima"]) { NSLog(@"位置移動動畫執行結束"); } else if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"TransformAnima"]){ NSLog(@"旋轉動畫執行結束"); } }
解決循環引用問題
由於CAAnimation的delegate使用的strong類型,

所以在全局變量如下設置時會產生循環引用的情況
self.animation.delegate = self;//可通過復用dealloc方法來驗證
解決方案
- 聲明一個單獨的類實現delegate的回調
//.h #import <UIKit/UIKit.h> @interface AnimationDelegate : NSObject @end //.m #import "AnimationDelegate.h" @implementation AnimationDelegate - (void)animationDidStart:(CAAnimation *)anim { NSLog(@"Animation Start"); } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { NSLog(@"Animation Stop"); } - (void)dealloc { NSLog(@"Delegate Dealloc"); } @end
使用方式
self.animation.delegate = [[AnimationDelegate alloc]init];
- 使用
NSProxy
來解決
該類可直接引用YYKit中的YYWeakProxy
類
使用方法self.animation.delegate = [YYWeakProxy proxyWithTarget:self];
一些常用的animationWithKeyPath值的總結
值 | 說明 | 使用形式 |
---|---|---|
transform.scale | 比例轉化 | @(0.8) |
transform.scale.x | 寬的比例 | @(0.8) |
transform.scale.y | 高的比例 | @(0.8) |
transform.rotation.x | 圍繞x軸旋轉 | @(M_PI) |
transform.rotation.y | 圍繞y軸旋轉 | @(M_PI) |
transform.rotation.z | 圍繞z軸旋轉 | @(M_PI) |
cornerRadius | 圓角的設置 | @(50) |
backgroundColor | 背景顏色的變化 | (id)[UIColor purpleColor].CGColor |
bounds | 大小,中心不變 | [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)]; |
position | 位置(中心點的改變) | [NSValue valueWithCGPoint:CGPointMake(300, 300)]; |
contents | 內容,比如UIImageView的圖片 | imageAnima.toValue = (id)[UIImage imageNamed:@"to"].CGImage; |
opacity | 透明度 | @(0.7) |
contentsRect.size.width | 橫向拉伸縮放 | @(0.4)最好是0~1之間的 |