實(shí)例化
使用方法animationWithKeyPath:
對 CABasicAnimation進(jìn)行實(shí)例化绳瘟,并指定Layer的屬性作為關(guān)鍵路徑進(jìn)行注冊。
//圍繞y軸旋轉(zhuǎn)
CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
設(shè)定動(dòng)畫
設(shè)定動(dòng)畫的屬性和說明
屬性 | 說明 |
---|---|
duration | 動(dòng)畫的時(shí)長 |
repeatCount | 重復(fù)的次數(shù)糖声。不停重復(fù)設(shè)置為 HUGE_VALF |
repeatDuration | 設(shè)置動(dòng)畫的時(shí)間。在該時(shí)間內(nèi)動(dòng)畫一直執(zhí)行琉苇,不計(jì)次數(shù)。 |
beginTime | 指定動(dòng)畫開始的時(shí)間并扇。從開始延遲幾秒的話抡诞,設(shè)置為【CACurrentMediaTime() + 秒數(shù)】 的方式 |
timingFunction | 設(shè)置動(dòng)畫的速度變化 |
autoreverses | 動(dòng)畫結(jié)束時(shí)是否執(zhí)行逆動(dòng)畫 |
fromValue | 所改變屬性的起始值 |
toValue | 所改變屬性的結(jié)束時(shí)的值 |
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;
防止動(dòng)畫結(jié)束后回到初始狀態(tài)
只需設(shè)置removedOnCompletion
、fillMode
兩個(gè)屬性就可以了昼汗。
transformAnima.removedOnCompletion = NO;
transformAnima.fillMode = kCAFillModeForwards;
解釋:為什么動(dòng)畫結(jié)束后返回原狀態(tài)?
首先我們需要搞明白一點(diǎn)的是蛙吏,layer動(dòng)畫運(yùn)行的過程是怎樣的?其實(shí)在我們給一個(gè)視圖添加layer動(dòng)畫時(shí)鸦做,真正移動(dòng)并不是我們的視圖本身,而是 presentation layer 的一個(gè)緩存崩侠。動(dòng)畫開始時(shí) presentation layer開始移動(dòng),原始layer隱藏却音,動(dòng)畫結(jié)束時(shí),presentation layer從屏幕上移除系瓢,原始layer顯示句灌。這就解釋了為什么我們的視圖在動(dòng)畫結(jié)束后又回到了原來的狀態(tài),因?yàn)樗揪蜎]動(dòng)過胰锌。
這個(gè)同樣也可以解釋為什么在動(dòng)畫移動(dòng)過程中,我們?yōu)楹尾荒軐ζ溥M(jìn)行任何操作资昧。
所以在我們完成layer動(dòng)畫之后,最好將我們的layer屬性設(shè)置為我們最終狀態(tài)的屬性撤缴,然后將presentation layer 移除掉。
添加動(dòng)畫
[self.imageView.layer addAnimation:transformAnima forKey:@"A"];
需要注意的兩點(diǎn)
- 一個(gè) CABasicAniamtion 的實(shí)例對象只是一個(gè)數(shù)據(jù)模型屈呕,和他綁定到哪一個(gè)layer上是沒有關(guān)系的
- 方法
addAnimation:forKey:
是將 CABasicAniamtion 對象進(jìn)行了 copy 操作的棺亭。所以在將其添加到一個(gè)layer上之后,我們還是將其再次添加到另一個(gè)layer上的侦铜。
fillMode屬性的理解
該屬性定義了你的動(dòng)畫在開始和結(jié)束時(shí)的動(dòng)作。默認(rèn)值是 kCAFillModeRemoved
钉稍。
取值的解釋
- kCAFillModeRemoved 設(shè)置為該值贡未,動(dòng)畫將在設(shè)置的 beginTime 開始執(zhí)行(如沒有設(shè)置beginTime屬性,則動(dòng)畫立即執(zhí)行)俊卤,動(dòng)畫執(zhí)行完成后將會(huì)layer的改變恢復(fù)原狀。
- kCAFillModeForwards 設(shè)置為該值岂昭,動(dòng)畫即使之后layer的狀態(tài)將保持在動(dòng)畫的最后一幀,而removedOnCompletion的默認(rèn)屬性值是 YES约啊,所以為了使動(dòng)畫結(jié)束之后layer保持結(jié)束狀態(tài),應(yīng)將removedOnCompletion設(shè)置為NO恰矩。
- kCAFillModeBackwards 設(shè)置為該值憎蛤,將會(huì)立即執(zhí)行動(dòng)畫的第一幀,不論是否設(shè)置了 beginTime屬性俩檬。觀察發(fā)現(xiàn),設(shè)置該值奥洼,剛開始視圖不見,還不知道應(yīng)用在哪里灵奖。
- kCAFillModeBoth 該值是 kCAFillModeForwards 和 kCAFillModeBackwards的組合狀態(tài)
Animation Easing的使用
也即是屬性timingFunction
值的設(shè)定估盘,有種方式來獲取屬性值
(1)使用方法functionWithName:
這種方式很簡單,這里只是簡單說明一下取值的含義:
kCAMediaTimingFunctionLinear 傳這個(gè)值遣妥,在整個(gè)動(dòng)畫時(shí)間內(nèi)動(dòng)畫都是以一個(gè)相同的速度來改變。也就是勻速運(yùn)動(dòng)爱态。
-
kCAMediaTimingFunctionEaseIn 使用該值,動(dòng)畫開始時(shí)會(huì)較慢锦担,之后動(dòng)畫會(huì)加速。
-
kCAMediaTimingFunctionEaseOut 使用該值洞渔,動(dòng)畫在開始時(shí)會(huì)較快,之后動(dòng)畫速度減慢磁椒。
-
kCAMediaTimingFunctionEaseInEaseOut 使用該值,動(dòng)畫在開始和結(jié)束時(shí)速度較慢浆熔,中間時(shí)間段內(nèi)速度較快。
(2)使用方法functionWithControlPoints: : : :
實(shí)現(xiàn)慎皱,這個(gè)之后再說,占個(gè)坑先宝冕。
其他的一些設(shè)置屬性
- repeatCount 設(shè)置動(dòng)畫的執(zhí)行次數(shù)
- autoreverses 默認(rèn)值為 NO,將其設(shè)置為 YES
- speed 改變動(dòng)畫的速度 可以直接設(shè)置動(dòng)畫上的speed屬性地梨,這樣只有這個(gè)動(dòng)畫速度缔恳。
animation.speed = 2;
或者在layer上設(shè)置speed屬性,這樣在該視圖上的所有動(dòng)畫都提速歉甚,該視圖上的所有子視圖上的動(dòng)畫也會(huì)提速。
speed兩點(diǎn)需注意的:
(1) 如果設(shè)置動(dòng)畫時(shí)間為4s纸泄,speed設(shè)置為2,則動(dòng)畫只需2s即可執(zhí)行完聘裁。
(2)如果同時(shí)設(shè)置了動(dòng)畫的speed和layer 的speed,則實(shí)際的speed為兩者相乘衡便。
使用總結(jié)
- 在動(dòng)畫執(zhí)行完成之后,最好還是將動(dòng)畫移除掉镣陕。也就是盡量不要設(shè)置
removedOnCompletion
屬性為NO -
fillMode
盡量取默認(rèn)值就好了,不要去設(shè)置它的值岂嗓。只有在極個(gè)別的情況下我們會(huì)修改它的值,以后會(huì)說到摄闸,這里先占個(gè)坑善镰。 - 解決有時(shí)視圖會(huì)閃動(dòng)一下的問題年枕,我們可以將layer的屬性值設(shè)置為我們的動(dòng)畫最后要達(dá)到的值乎完,然后再給我們的視圖添加layer動(dòng)畫。
例子(移動(dòng)動(dòng)畫實(shí)現(xiàn))
直接上代碼
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"];
組合動(dòng)畫的實(shí)現(xiàn)
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"];
動(dòng)畫開始和結(jié)束時(shí)的事件
為了獲取動(dòng)畫的開始和結(jié)束事件摩桶,需要實(shí)現(xiàn)協(xié)議
positionAnima.delegate = self;
代理方法實(shí)現(xiàn)
//動(dòng)畫開始時(shí)
- (void)animationDidStart:(CAAnimation *)anim
{
NSLog(@"開始了");
}
//動(dòng)畫結(jié)束時(shí)
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
//方法中的flag參數(shù)表明了動(dòng)畫是自然結(jié)束還是被打斷,比如調(diào)用了removeAnimationForKey:方法或removeAnimationForKey方法帽揪,flag為NO,如果是正常結(jié)束转晰,flag為YES。
NSLog(@"結(jié)束了");
}
其實(shí)比較重要的是有多個(gè)動(dòng)畫的時(shí)候如何在代理方法中區(qū)分不同的動(dòng)畫
兩種方式
方式一:
如果我們添加動(dòng)畫的視圖是全局變量查邢,可使用該方法。
添加動(dòng)畫時(shí)扰藕,我們使用了
[self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];
所以,可根據(jù)key來區(qū)分不同的動(dòng)畫
//動(dòng)畫開始時(shí)
- (void)animationDidStart:(CAAnimation *)anim
{
if ([anim isEqual:[self.imageView.layer animationForKey:@"Animation"]]) {
NSLog(@"動(dòng)畫組執(zhí)行了");
}
}
Note:把動(dòng)畫存儲(chǔ)為一個(gè)屬性然后再回調(diào)中比較未桥,用來判定是哪個(gè)動(dòng)畫是不可行的。應(yīng)為委托傳入的動(dòng)畫參數(shù)是原始值的一個(gè)深拷貝冬耿,不是同一個(gè)值
方式二
添加動(dòng)畫的視圖是局部變量時(shí),可使用該方法
添加動(dòng)畫給動(dòng)畫設(shè)置key-value對
[positionAnima setValue:@"PositionAnima" forKey:@"AnimationKey"];
[transformAnima setValue:@"TransformAnima" forKey:@"AnimationKey"];
所以淆党,可以根據(jù)key中不同的值來進(jìn)行區(qū)分不同的動(dòng)畫
//動(dòng)畫結(jié)束時(shí)
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"PositionAnima"]) {
NSLog(@"位置移動(dòng)動(dòng)畫執(zhí)行結(jié)束");
}
else if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"TransformAnima"]){
NSLog(@"旋轉(zhuǎn)動(dòng)畫執(zhí)行結(jié)束");
}
}
解決循環(huán)引用問題
由于CAAnimation的delegate使用的strong類型讶凉,
所以在全局變量如下設(shè)置時(shí)會(huì)產(chǎn)生循環(huán)引用的情況
self.animation.delegate = self;//可通過復(fù)用dealloc方法來驗(yàn)證
解決方案
- 聲明一個(gè)單獨(dú)的類實(shí)現(xiàn)delegate的回調(diào)
//.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值的總結(jié)
值 | 說明 | 使用形式 |
---|---|---|
transform.scale | 比例轉(zhuǎn)化 | @(0.8) |
transform.scale.x | 寬的比例 | @(0.8) |
transform.scale.y | 高的比例 | @(0.8) |
transform.rotation.x | 圍繞x軸旋轉(zhuǎn) | @(M_PI) |
transform.rotation.y | 圍繞y軸旋轉(zhuǎn) | @(M_PI) |
transform.rotation.z | 圍繞z軸旋轉(zhuǎn) | @(M_PI) |
cornerRadius | 圓角的設(shè)置 | @(50) |
backgroundColor | 背景顏色的變化 | (id)[UIColor purpleColor].CGColor |
bounds | 大小,中心不變 | [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)]; |
position | 位置(中心點(diǎn)的改變) | [NSValue valueWithCGPoint:CGPointMake(300, 300)]; |
contents | 內(nèi)容荷憋,比如UIImageView的圖片 | imageAnima.toValue = (id)[UIImage imageNamed:@"to"].CGImage; |
opacity | 透明度 | @(0.7) |
contentsRect.size.width | 橫向拉伸縮放 | @(0.4)最好是0~1之間的 |