目錄
- 一 Core Animation
- 二 核心動(dòng)畫
- 2.1 基礎(chǔ)動(dòng)畫
- 2.2 關(guān)鍵幀動(dòng)畫
- 2.3 動(dòng)畫組
- 2.4 轉(zhuǎn)場(chǎng)動(dòng)畫
- 2.5 逐幀動(dòng)畫
- 三 UIView動(dòng)畫封裝
- 3.1 基礎(chǔ)動(dòng)畫
- 3.2 彈簧動(dòng)畫
- 3.3 關(guān)鍵幀動(dòng)畫
- 3.4 轉(zhuǎn)場(chǎng)動(dòng)畫
一 Core Animation
大家都知道在iOS中實(shí)現(xiàn)一個(gè)動(dòng)畫相當(dāng)簡(jiǎn)單膛虫,只要調(diào)用UIView的塊代碼即可實(shí)現(xiàn)一個(gè)動(dòng)畫效果,這在其他系統(tǒng)開發(fā)中基本不可能實(shí)現(xiàn)睬关。下面通過(guò)一個(gè)簡(jiǎn)單的UIView進(jìn)行一個(gè)圖片放大動(dòng)畫效果演示:
- (void)addAnimation {
UIImage *image=[UIImage imageNamed:@"girl"];
UIImageView *imageView=[[UIImageView alloc]init];
imageView.image=image;
imageView.frame=CGRectMake(120, 140, 80, 80);
imageView.center = self.view.center;
[self.view addSubview:imageView];
//兩秒后開始一個(gè)持續(xù)一分鐘的動(dòng)畫
[UIView animateWithDuration:1 delay:2 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
imageView.frame=CGRectMake(80, 100, 160, 160);
} completion:nil];
}
使用上面UIView封裝的方法進(jìn)行動(dòng)畫設(shè)置固然十分方便魁蒜,但是具體動(dòng)畫如何實(shí)現(xiàn)我們是不清楚的,而且上面的代碼還有一些問(wèn)題是無(wú)法解決的陆赋,例如:如何控制動(dòng)畫的暫停轧膘?如何進(jìn)行動(dòng)畫的組合败富?
這里就需要了解iOS的核心動(dòng)畫Core Animation(包含在Quartz Core框架中)
二 核心動(dòng)畫
在iOS中核心動(dòng)畫分為幾類:
- 基礎(chǔ)動(dòng)畫
- 關(guān)鍵幀動(dòng)畫
- 動(dòng)畫組
- 轉(zhuǎn)場(chǎng)動(dòng)畫
- 逐幀動(dòng)畫
各個(gè)類的關(guān)系大致如下:
-
CAAnimation
:核心動(dòng)畫的基礎(chǔ)類讥此,不能直接使用拢锹,負(fù)責(zé)動(dòng)畫運(yùn)行時(shí)間、速度的控制萄喳,本身實(shí)現(xiàn)了CAMediaTiming協(xié)議卒稳。 -
CAPropertyAnimation
:屬性動(dòng)畫的基類(通過(guò)屬性進(jìn)行動(dòng)畫設(shè)置,注意是可動(dòng)畫屬性)他巨,不能直接使用充坑。 -
CAAnimationGroup
:動(dòng)畫組,動(dòng)畫組是一種組合模式設(shè)計(jì)染突,可以通過(guò)組合動(dòng)畫組來(lái)進(jìn)行所有動(dòng)畫行為的統(tǒng)一控制捻爷,組中的所有動(dòng)畫效果可以并發(fā)執(zhí)行。 -
CATransition
:轉(zhuǎn)場(chǎng)動(dòng)畫觉痛,通過(guò)濾鏡進(jìn)行動(dòng)畫效果設(shè)置役衡。 -
CABasicAnimation
:基礎(chǔ)動(dòng)畫,通過(guò)屬性修改進(jìn)行動(dòng)畫參數(shù)控制薪棒,只有初始狀態(tài)和結(jié)束狀態(tài)。 -
CAKeyframeAnimation
:關(guān)鍵幀動(dòng)畫榕莺,同樣是通過(guò)屬性進(jìn)行動(dòng)畫參數(shù)控制俐芯,但是同基礎(chǔ)動(dòng)畫不同的是它可以有多個(gè)狀態(tài)控制。
基礎(chǔ)動(dòng)畫钉鸯、關(guān)鍵幀動(dòng)畫都屬于屬性動(dòng)畫吧史,就是通過(guò)修改屬性值產(chǎn)生動(dòng)畫效果,開發(fā)人員只需要設(shè)置初始值和結(jié)束值唠雕,中間的過(guò)程動(dòng)畫(又叫“補(bǔ)間動(dòng)畫”)由系統(tǒng)自動(dòng)計(jì)算產(chǎn)生贸营。和基礎(chǔ)動(dòng)畫不同的是關(guān)鍵幀動(dòng)畫可以設(shè)置多個(gè)屬性值,每?jī)蓚€(gè)屬性中間的補(bǔ)間動(dòng)畫由系統(tǒng)自動(dòng)完成岩睁,因此從這個(gè)角度而言基礎(chǔ)動(dòng)畫又可以看成是有兩個(gè)關(guān)鍵幀的關(guān)鍵幀動(dòng)畫钞脂。
2.1 基礎(chǔ)動(dòng)畫
在開發(fā)過(guò)程中很多情況下通過(guò)基礎(chǔ)動(dòng)畫就可以滿足開發(fā)需求,如果不使用UIView封裝的方法捕儒,動(dòng)畫創(chuàng)建一般分為以下幾步:
- 1.初始化動(dòng)畫并設(shè)置動(dòng)畫屬性
- 2.設(shè)置動(dòng)畫屬性初始值(可以省略)冰啃、結(jié)束值以及其他動(dòng)畫屬性
- 3.給圖層添加動(dòng)畫
下面以一個(gè)移動(dòng)動(dòng)畫為例進(jìn)行演示邓夕,在這個(gè)例子中點(diǎn)擊屏幕哪個(gè)位置落花將飛向哪里
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"基礎(chǔ)動(dòng)畫";
[self addSubLayer];
}
// 添加一個(gè)圖層
- (void)addSubLayer {
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage = [UIImage imageNamed:@"women"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
// 自定義一個(gè)圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 35, 35);
_layer.position=CGPointMake(50, 150);
_layer.contents=(id)[UIImage imageNamed:@"girl"].CGImage;
[self.view.layer addSublayer:_layer];
}
#pragma mark 移動(dòng)動(dòng)畫
-(void)translatonAnimation:(CGPoint)location{
//1.創(chuàng)建動(dòng)畫并指定動(dòng)畫屬性
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
//2.設(shè)置動(dòng)畫屬性初始值和結(jié)束值
//basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設(shè)置,默認(rèn)為圖層初始狀態(tài)
basicAnimation.toValue = [NSValue valueWithCGPoint:location];
//設(shè)置其他動(dòng)畫屬性
basicAnimation.duration = 2.0;//動(dòng)畫時(shí)間5秒
// basicAnimation.repeatCount=HUGE_VALF;//設(shè)置重復(fù)次數(shù),HUGE_VALF可看做無(wú)窮大阎毅,起到循環(huán)動(dòng)畫的效果
basicAnimation.removedOnCompletion = NO;//運(yùn)行一次是否移除動(dòng)畫
//3.添加動(dòng)畫到圖層焚刚,注意key相當(dāng)于給動(dòng)畫進(jìn)行命名,以后獲得該動(dòng)畫時(shí)可以使用此名稱獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}
// 指哪飛哪
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint point = [touch locationInView:self.view];
[self translatonAnimation:point];
}
前面說(shuō)過(guò)圖層動(dòng)畫的本質(zhì)就是將圖層內(nèi)部的內(nèi)容轉(zhuǎn)化為位圖經(jīng)硬件操作形成一種動(dòng)畫效果扇调,其實(shí)圖層本身
并沒有任何的變化矿咕。上面的動(dòng)畫中圖層并沒有因?yàn)閯?dòng)畫效果而改變它的位置(對(duì)于縮放動(dòng)畫其大小也是不會(huì)改變的),所以動(dòng)畫完成之后圖層還是在原來(lái)的顯示位置
沒有任何變化狼钮,如果這個(gè)圖層在一個(gè)UIView中你會(huì)發(fā)現(xiàn)在UIView移動(dòng)過(guò)程中你要觸發(fā)UIView的點(diǎn)擊事件也只能點(diǎn)擊原來(lái)的位置(即使它已經(jīng)運(yùn)動(dòng)到了別的位置)碳柱,因?yàn)樗奈恢脧膩?lái)沒有變過(guò)。當(dāng)然解決這個(gè)問(wèn)題方法比較多燃领,這里不妨在動(dòng)畫完成之后重新設(shè)置它的位置士聪。
如過(guò)給動(dòng)畫設(shè)置一個(gè)代理去監(jiān)聽動(dòng)畫的開始和結(jié)束事件,在動(dòng)畫開始前給動(dòng)畫添加一個(gè)自定義屬性KCBasicAnimationLocation
存儲(chǔ)動(dòng)畫終點(diǎn)位置猛蔽,然后在動(dòng)畫結(jié)束后設(shè)置動(dòng)畫的位置為終點(diǎn)位置剥悟。
如果運(yùn)行上面的代碼大家可能會(huì)發(fā)現(xiàn)另外一個(gè)問(wèn)題,那就是動(dòng)畫運(yùn)行完成后會(huì)重新從起始點(diǎn)運(yùn)動(dòng)到終點(diǎn)曼库。這個(gè)問(wèn)題產(chǎn)生的原因就是前面提到的区岗,對(duì)于非根圖層,設(shè)置圖層的可動(dòng)畫屬性(在動(dòng)畫結(jié)束后重新設(shè)置了position毁枯,而position是可動(dòng)畫屬性)會(huì)產(chǎn)生動(dòng)畫效果慈缔。解決這個(gè)問(wèn)題有兩種辦法:關(guān)閉圖層隱式動(dòng)畫、設(shè)置動(dòng)畫圖層為根圖層种玛。顯然這里不能采取后者藐鹤,因?yàn)楦鶊D層當(dāng)前已經(jīng)作為動(dòng)畫的背景。
法一
// 設(shè)置代理
basicAnimation.delegate = self;
//存儲(chǔ)當(dāng)前位置在動(dòng)畫結(jié)束后使用
[basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
#pragma mark - 動(dòng)畫代理方法
// 動(dòng)畫開始
-(void)animationDidStart:(CAAnimation *)anim{
NSLog(@"animation(%@) start.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
NSLog(@"%@",[_layer animationForKey:@"KCBasicAnimation_Translation"]);//通過(guò)前面的設(shè)置的key獲得動(dòng)畫
}
// 動(dòng)畫結(jié)束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
_layer.position = [[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
}
發(fā)現(xiàn)動(dòng)畫完畢后赂韵,馬上回到起點(diǎn)然后直接跳轉(zhuǎn)到終點(diǎn)娱节。
法二
要關(guān)閉隱式動(dòng)畫需要用到動(dòng)畫事務(wù)CATransaction,在事務(wù)內(nèi)將隱式動(dòng)畫關(guān)閉祭示。
// 動(dòng)畫結(jié)束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
//開啟事務(wù)
[CATransaction begin];
//禁用隱式動(dòng)畫
[CATransaction setDisableActions:YES];
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
//提交事務(wù)
[CATransaction commit];
}
動(dòng)畫完畢后停留在終點(diǎn)
上面通過(guò)在animationDidStop中重新設(shè)置動(dòng)畫的位置主要為了說(shuō)明隱式動(dòng)畫關(guān)閉和動(dòng)畫事件之間傳參的內(nèi)容肄满,發(fā)現(xiàn)這種方式有可能在動(dòng)畫運(yùn)行完之后出現(xiàn)從原點(diǎn)瞬間回到終點(diǎn)的過(guò)程,最早在調(diào)試的時(shí)候沒有發(fā)現(xiàn)這個(gè)問(wèn)題质涛,其實(shí)解決這個(gè)問(wèn)題并不難稠歉,首先必須設(shè)置fromValue,其次在動(dòng)畫開始前設(shè)置動(dòng)畫position為終點(diǎn)位置(當(dāng)然也必須關(guān)閉隱式動(dòng)畫)汇陆。但是這里主要還是出于學(xué)習(xí)的目的怒炸,真正開發(fā)的時(shí)候做平移動(dòng)畫直接使用隱式動(dòng)畫即可,沒有必要那么麻煩瞬测。
- 圖層的形變都是基于錨點(diǎn)進(jìn)行的横媚。例如旋轉(zhuǎn)纠炮,旋轉(zhuǎn)的中心點(diǎn)就是圖層的錨點(diǎn)。
添加旋轉(zhuǎn)動(dòng)畫
#pragma mark 旋轉(zhuǎn)動(dòng)畫
- (void)rotationAnimation{
//1.創(chuàng)建動(dòng)畫并指定動(dòng)畫屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//2.設(shè)置動(dòng)畫屬性初始值灯蝴、結(jié)束值
// basicAnimation.fromValue=[NSNumber numberWithInt:M_PI_2];
basicAnimation.toValue = [NSNumber numberWithFloat:M_PI_2 * 3];
//設(shè)置其他動(dòng)畫屬性
basicAnimation.duration = 2.0;
basicAnimation.autoreverses = true;//旋轉(zhuǎn)后再旋轉(zhuǎn)到原來(lái)的位置
//4.添加動(dòng)畫到圖層恢口,注意key相當(dāng)于給動(dòng)畫進(jìn)行命名,以后獲得該動(dòng)畫時(shí)可以使用此名稱獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}
上面代碼中結(jié)合兩種動(dòng)畫操作穷躁,需要注意的是只給移動(dòng)動(dòng)畫設(shè)置了代理耕肩,在旋轉(zhuǎn)動(dòng)畫中并沒有設(shè)置代理,否則代理方法會(huì)執(zhí)行兩遍问潭。由于旋轉(zhuǎn)動(dòng)畫會(huì)無(wú)限循環(huán)執(zhí)行(上面設(shè)置了重復(fù)次數(shù)無(wú)窮大)猿诸,并且兩個(gè)動(dòng)畫的執(zhí)行時(shí)間沒有必然的關(guān)系,這樣一來(lái)移動(dòng)停止后可能還在旋轉(zhuǎn)狡忙,為了讓移動(dòng)動(dòng)畫停止后旋轉(zhuǎn)動(dòng)畫梳虽,停止就需要使用到動(dòng)畫的暫停和恢復(fù)方法。
下面的代碼演示了移動(dòng)動(dòng)畫結(jié)束后旋轉(zhuǎn)動(dòng)畫暫停灾茁,并且當(dāng)再次點(diǎn)擊動(dòng)畫時(shí)旋轉(zhuǎn)恢復(fù)的過(guò)程(注意在移動(dòng)過(guò)程中如果再次點(diǎn)擊屏幕可以暫停移動(dòng)和旋轉(zhuǎn)動(dòng)畫窜觉,再次點(diǎn)擊可以恢復(fù)兩種動(dòng)畫。但是當(dāng)移動(dòng)結(jié)束后觸發(fā)了移動(dòng)動(dòng)畫的完成事件如果再次點(diǎn)擊屏幕則只能恢復(fù)旋轉(zhuǎn)動(dòng)畫北专,因?yàn)榇藭r(shí)移動(dòng)動(dòng)畫已經(jīng)結(jié)束而不是暫停禀挫,無(wú)法再恢復(fù))。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = touches.anyObject;
CGPoint position = [touch locationInView:self.view];
CAAnimation *animation = [_layer animationForKey:@"ZXBasicAnimation_rotation"];
//創(chuàng)建并開始動(dòng)畫
if (animation) {
if (_layer.speed == 0) {
[self animationResume];
}else{
[self animationPause];
}
} else {
[self translatonAnimation:position];
[self rotationAnimation];
}
}
#pragma mark - 暫停 | 繼續(xù)
- (void)animationPause {
//取得指定圖層動(dòng)畫的媒體時(shí)間拓颓,后面參數(shù)用于指定子圖層语婴,這里不需要
CFTimeInterval interval = [_layer convertTime:CACurrentMediaTime() fromLayer:nil];
//設(shè)置時(shí)間偏移量,保證暫停時(shí)停留在旋轉(zhuǎn)的位置
_layer.timeOffset = interval;
//速度設(shè)置為零驶睦,暫停動(dòng)畫
_layer.speed = 0;
}
- (void)animationResume {
//獲取暫停的時(shí)間
CFTimeInterval beginTime = CACurrentMediaTime() - _layer.timeOffset;
//設(shè)置偏移量
_layer.timeOffset = 0;
//設(shè)置開始時(shí)間
_layer.beginTime = beginTime;
//設(shè)置動(dòng)畫速度砰左,開始運(yùn)動(dòng)
_layer.speed = 1.0;
}
注意
: 動(dòng)畫暫停針對(duì)的是圖層而不是圖層中的某個(gè)動(dòng)畫。
要做無(wú)限循環(huán)的動(dòng)畫场航,動(dòng)畫的removedOnCompletion屬性必須設(shè)置為NO菜职,否則運(yùn)行一次動(dòng)畫就會(huì)銷毀。
2.2 關(guān)鍵幀動(dòng)畫
熟悉flash開發(fā)的朋友對(duì)于關(guān)鍵幀動(dòng)畫應(yīng)該不陌生旗闽,這種動(dòng)畫方式在flash開發(fā)中經(jīng)常用到。關(guān)鍵幀動(dòng)畫就是在動(dòng)畫控制過(guò)程中開發(fā)者指定主要的動(dòng)畫狀態(tài)蜜另,至于各個(gè)狀態(tài)間動(dòng)畫如何進(jìn)行則由系統(tǒng)自動(dòng)運(yùn)算補(bǔ)充(每?jī)蓚€(gè)關(guān)鍵幀之間系統(tǒng)形成的動(dòng)畫稱為“補(bǔ)間動(dòng)畫”)适室,這種動(dòng)畫的好處就是開發(fā)者不用逐個(gè)控制每個(gè)動(dòng)畫幀,而只要關(guān)心幾個(gè)關(guān)鍵幀的狀態(tài)即可举瑰。
關(guān)鍵幀動(dòng)畫開發(fā)分為兩種形式:
- 一種是通過(guò)設(shè)置不同的屬性值進(jìn)行關(guān)鍵幀控制捣辆,
- 另一種是通過(guò)繪制路徑進(jìn)行關(guān)鍵幀控制。
后者優(yōu)先級(jí)高于前者此迅,如果設(shè)置了路徑則屬性值就不再起作用汽畴。
在這里需要設(shè)置四個(gè)關(guān)鍵幀(如圖中四個(gè)關(guān)鍵點(diǎn))旧巾,具體代碼如下(動(dòng)畫創(chuàng)建過(guò)程同基本動(dòng)畫基本完全一致):
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"關(guān)鍵幀動(dòng)畫";
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage* backgroundImage = [UIImage imageNamed:@"snow"];
self.view.backgroundColor = [UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
self.layer = [[CALayer alloc]init];
self.layer.bounds= CGRectMake(0, 0, 10, 20);
self.layer.position = CGPointMake(50, 150);
self.layer.contents = (id)[UIImage imageNamed:@"snowflake"].CGImage;
[self.view.layer addSublayer:self.layer];
//創(chuàng)建動(dòng)畫
[self translationAnimation_values];
}
- (void)translationAnimation_values {
//1.創(chuàng)建關(guān)鍵幀動(dòng)畫并設(shè)置動(dòng)畫屬性
CAKeyframeAnimation* keyFrameAnimation =[CAKeyframeAnimation animationWithKeyPath:@"position"];
//2.設(shè)置關(guān)鍵幀,這里有四個(gè)關(guān)鍵幀
NSValue* key1 = [NSValue valueWithCGPoint:self.layer.position];//對(duì)于關(guān)鍵幀動(dòng)畫初始值不能省略
NSValue* key2 = [NSValue valueWithCGPoint:CGPointMake(80, 220)];
NSValue* key3 = [NSValue valueWithCGPoint:CGPointMake(45, 320)];
NSValue* key4 = [NSValue valueWithCGPoint:CGPointMake(75, 420)];
//設(shè)置其他屬性
keyFrameAnimation.values = @[key1,key2,key3,key4];
keyFrameAnimation.duration = 7;
//keyFrameAnimation.beginTime = CACurrentMediaTime() + 2;//設(shè)置延遲2秒執(zhí)行
keyFrameAnimation.keyTimes = @[@(2/7.0),@(5.5/7),@(6.25/7),@1.0];
//3.添加動(dòng)畫到圖層忍些,添加動(dòng)畫后就會(huì)行動(dòng)畫
[self.layer addAnimation:keyFrameAnimation forKey:@"myAnimation"];
}
上面的方式固然比前面使用基礎(chǔ)動(dòng)畫效果要好一些鲁猩,但其實(shí)還是存在問(wèn)題,那就是落花飛落的路徑是直線的罢坝,當(dāng)然這個(gè)直線是根據(jù)程序中設(shè)置的四個(gè)關(guān)鍵幀自動(dòng)形成的廓握,那么如何讓它沿著曲線飄落呢?這就是第二種類型的關(guān)鍵幀動(dòng)畫嘁酿,通過(guò)描繪路徑進(jìn)行關(guān)鍵幀動(dòng)畫控制隙券。假設(shè)讓落花沿著下面的曲線路徑飄落:
- 這是一條貝塞爾曲線
- (void)translationAnimation_path {
//1.創(chuàng)建關(guān)鍵幀動(dòng)畫并設(shè)置動(dòng)畫屬性
CAKeyframeAnimation* keyFrameAnimation =[CAKeyframeAnimation animationWithKeyPath:@"position"];
//2.設(shè)置路徑
//貝塞爾曲線
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, self.layer.position.x, self.layer.position.y);
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, 55, 400);//繪制二次貝塞爾曲線
keyFrameAnimation.path = path;
CGPathRelease(path);
keyFrameAnimation.duration = 5.0;
// keyFrameAnimation.beginTime = CACurrentMediaTime() + 2;//設(shè)置延遲2秒執(zhí)行
//3.添加動(dòng)畫到圖層,添加動(dòng)畫后就會(huì)行動(dòng)畫
[self.layer addAnimation:keyFrameAnimation forKey:@"myAnimation"];
}
但是這里需要注意闹司,對(duì)于路徑類型的關(guān)鍵幀動(dòng)畫系統(tǒng)是從描繪路徑的位置開始路徑娱仔,直到路徑結(jié)束。如果上面的路徑不是貝塞爾曲線而是矩形路徑那么它會(huì)從矩形的左上角開始運(yùn)行游桩,順時(shí)針一周回到左上角牲迫;如果指定的路徑是一個(gè)橢圓,那么動(dòng)畫運(yùn)行的路徑是從橢圓右側(cè)開始(0度)順時(shí)針一周回到右側(cè)众弓。
-
補(bǔ)充:其他屬性
在關(guān)鍵幀動(dòng)畫中還有一些動(dòng)畫屬性初學(xué)者往往比較容易混淆恩溅,這里專門針對(duì)這些屬性做一下介紹。 -
keyTimes
:各個(gè)關(guān)鍵幀的時(shí)間控制谓娃。前面使用values設(shè)置了四個(gè)關(guān)鍵幀脚乡,默認(rèn)情況下每?jī)蓭g的間隔為:8/(4-1)秒。如果想要控制動(dòng)畫從第一幀到第二針占用時(shí)間4秒滨达,從第二幀到第三幀時(shí)間為2秒奶稠,而從第三幀到第四幀時(shí)間2秒的話,就可以通過(guò)keyTimes進(jìn)行設(shè)置捡遍。keyTimes中存儲(chǔ)的是時(shí)間占用比例點(diǎn)锌订,此時(shí)可以設(shè)置keyTimes的值為0.0,0.5画株,0.75辆飘,1.0(當(dāng)然必須轉(zhuǎn)換為NSNumber),也就是說(shuō)1到2幀運(yùn)行到總時(shí)間的50%谓传,2到3幀運(yùn)行到總時(shí)間的75%蜈项,3到4幀運(yùn)行到8秒結(jié)束。 -
caculationMode
:動(dòng)畫計(jì)算模式续挟。還拿上面keyValues動(dòng)畫舉例紧卒,之所以1到2幀能形成連貫性動(dòng)畫而不是直接從第1幀經(jīng)過(guò)8/3秒到第2幀是因?yàn)閯?dòng)畫模式是連續(xù)的(值為kCAAnimationLinear,這是計(jì)算模式的默認(rèn)值)诗祸;而如果指定了動(dòng)畫模式為kCAAnimationDiscrete離散的那么你會(huì)看到動(dòng)畫從第1幀經(jīng)過(guò)8/3秒直接到第2幀跑芳,中間沒有任何過(guò)渡轴总。其他動(dòng)畫模式還有:kCAAnimationPaced(均勻執(zhí)行,會(huì)忽略keyTimes)博个、kCAAnimationCubic(平滑執(zhí)行怀樟,對(duì)于位置變動(dòng)關(guān)鍵幀動(dòng)畫運(yùn)行軌跡更平滑)、kCAAnimationCubicPaced(平滑均勻執(zhí)行)坡倔。
2.3 動(dòng)畫組
實(shí)際開發(fā)中一個(gè)物體的運(yùn)動(dòng)往往是復(fù)合運(yùn)動(dòng)漂佩,單一屬性的運(yùn)動(dòng)情況比較少,但恰恰屬性動(dòng)畫每次進(jìn)行動(dòng)畫設(shè)置時(shí)一次只能設(shè)置一個(gè)屬性進(jìn)行動(dòng)畫控制(不管是基礎(chǔ)動(dòng)畫還是關(guān)鍵幀動(dòng)畫都是如此)罪塔,這樣一來(lái)要做一個(gè)復(fù)合運(yùn)動(dòng)的動(dòng)畫就必須創(chuàng)建多個(gè)屬性動(dòng)畫進(jìn)行組合投蝉。對(duì)于一兩種動(dòng)畫的組合或許處理起來(lái)還比較容易,但是對(duì)于更多動(dòng)畫的組合控制往往會(huì)變得很麻煩征堪,動(dòng)畫組的產(chǎn)生就是基于這樣一種情況而產(chǎn)生的瘩缆。動(dòng)畫組是一系列動(dòng)畫的組合,凡是添加到動(dòng)畫組中的動(dòng)畫都受控于動(dòng)畫組佃蚜,這樣一來(lái)各類動(dòng)畫公共的行為就可以統(tǒng)一進(jìn)行控制而不必單獨(dú)設(shè)置庸娱,而且放到動(dòng)畫組中的各個(gè)動(dòng)畫可以并發(fā)執(zhí)行,共同構(gòu)建出復(fù)雜的動(dòng)畫效果谐算。
前面關(guān)鍵幀動(dòng)畫部分熟尉,路徑動(dòng)畫看起來(lái)效果雖然很流暢,但是落花本身的旋轉(zhuǎn)運(yùn)動(dòng)沒有了洲脂,這里不妨將基礎(chǔ)動(dòng)畫部分的旋轉(zhuǎn)動(dòng)畫和路徑關(guān)鍵幀動(dòng)畫進(jìn)行組合使得整個(gè)動(dòng)畫看起來(lái)更加的和諧斤儿、順暢。
#pragma mark - 添加基礎(chǔ)旋轉(zhuǎn)動(dòng)畫
- (CABasicAnimation*)rotationAnimation {
CABasicAnimation* basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
CGFloat toValue = M_PI_2*3;
basicAnimation.toValue = [NSNumber numberWithFloat:toValue];
basicAnimation.autoreverses = YES;
basicAnimation.repeatCount = HUGE_VAL;
basicAnimation.removedOnCompletion = NO;
[basicAnimation setValue:[NSNumber numberWithFloat:toValue] forKey:@"ZXBaiscAnimationProperty_toValue"];
return basicAnimation;
}
#pragma mark - 添加關(guān)鍵幀移動(dòng)動(dòng)畫
- (CAKeyframeAnimation*)translationAnimation {
CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
CGPoint endPoint = CGPointMake(55, 400);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, self.layer.position.x, self.layer.position.y);
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, endPoint.x, endPoint.y);
keyframeAnim.path = path;
CGPathRelease(path);
[keyframeAnim setValue:[NSValue valueWithCGPoint:endPoint] forKey:@"ZXKeyFrameAnimationProperty_endPosition"];
return keyframeAnim;
}
#pragma mark - 創(chuàng)建動(dòng)畫組
- (void)groupAnimation {
//1.創(chuàng)建動(dòng)畫組
CAAnimationGroup* animationGroup = [CAAnimationGroup animation];
//2.設(shè)置組中的動(dòng)畫和其他屬性
CABasicAnimation* basicAnimation = [self rotationAnimation];
CAKeyframeAnimation* keyFrameAnimation = [self translationAnimation];
animationGroup.animations = @[basicAnimation,keyFrameAnimation];
animationGroup.delegate = self;
animationGroup.duration = 10.0;//設(shè)置動(dòng)畫時(shí)間恐锦,如果動(dòng)畫組中動(dòng)畫已經(jīng)設(shè)置過(guò)動(dòng)畫屬性則不再生效
// animationGroup.beginTime = CACurrentMediaTime() + 5;//延遲5秒執(zhí)行
//3.給圖層添加動(dòng)畫
[self.layer addAnimation:animationGroup forKey:nil];
}
#pragma mark - CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
CAAnimationGroup* animationGroup = (CAAnimationGroup*)anim;
CABasicAnimation* basicAnimation = (CABasicAnimation*)animationGroup.animations[0];
CAKeyframeAnimation* keyFrameAnimation = (CAKeyframeAnimation*)animationGroup.animations[1];
CGFloat toValue = [[basicAnimation valueForKey:@"ZXBaiscAnimationProperty_toValue"] floatValue];
CGPoint endPoint = [[keyFrameAnimation valueForKey:@"ZXKeyFrameAnimationProperty_endPosition"] CGPointValue];
[CATransaction begin];
[CATransaction setDisableActions:YES];
//設(shè)置動(dòng)畫的最終狀態(tài)
self.layer.position = endPoint;
self.layer.transform = CATransform3DMakeRotation(toValue, 0, 0, 1);
[CATransaction commit];
}
2.4 轉(zhuǎn)場(chǎng)動(dòng)畫
轉(zhuǎn)場(chǎng)動(dòng)畫就是從一個(gè)場(chǎng)景以動(dòng)畫的形式過(guò)渡到另一個(gè)場(chǎng)景往果。轉(zhuǎn)場(chǎng)動(dòng)畫的使用一般分為以下幾個(gè)步驟:
- 創(chuàng)建轉(zhuǎn)場(chǎng)動(dòng)畫
- 設(shè)置轉(zhuǎn)場(chǎng)動(dòng)類型、子類型(可選)及其他屬性
- 設(shè)置轉(zhuǎn)場(chǎng)后的新視圖并添加動(dòng)畫到圖層
這里使用轉(zhuǎn)場(chǎng)動(dòng)畫利用一個(gè)UIImageView實(shí)現(xiàn)一個(gè)漂亮的無(wú)限循環(huán)圖片瀏覽器一铅。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"轉(zhuǎn)場(chǎng)動(dòng)畫";
self.myImageView = [[UIImageView alloc]initWithFrame:self.view.bounds];
self.myImageView.image = [UIImage imageNamed:@"0"];
self.myImageView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:_myImageView];
//添加手勢(shì)
UISwipeGestureRecognizer* leftSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
leftSwipeGesture.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:leftSwipeGesture];
UISwipeGestureRecognizer* rightSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
rightSwipeGesture.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:rightSwipeGesture];
}
#pragma mark - 向左滑動(dòng)瀏覽下張圖片
- (void)leftSwipe:(UISwipeGestureRecognizer*)gesture {
[self transitionAnimtion:YES];
}
#pragma mark - 向右滑動(dòng)瀏覽上張圖片
- (void)rightSwipe:(UISwipeGestureRecognizer*)gesture {
[self transitionAnimtion:NO];
}
#pragma mark - 添加轉(zhuǎn)場(chǎng)動(dòng)畫
- (void)transitionAnimtion:(BOOL)flag {
//1.創(chuàng)建轉(zhuǎn)場(chǎng)動(dòng)畫對(duì)象
CATransition* transition = [[CATransition alloc]init];
//2.設(shè)置動(dòng)畫類型陕贮,注意對(duì)于蘋果官方?jīng)]有公開的動(dòng)畫類型只能使用字符串,并沒有對(duì)應(yīng)的常亮使用
transition.type = @"cube";
//設(shè)置子類型
if (flag) {
transition.subtype = kCATransitionFromRight;
} else {
transition.subtype = kCATransitionFromLeft;
}
//設(shè)置動(dòng)畫時(shí)長(zhǎng)
transition.duration = 1.0f;
//3.設(shè)置轉(zhuǎn)場(chǎng)動(dòng)畫后,給新視圖添加轉(zhuǎn)場(chǎng)動(dòng)畫
self.myImageView.image = [self getImage:flag];
[self.myImageView.layer addAnimation:transition forKey:@"abc"];
}
- (UIImage*)getImage:(BOOL)flag {
if (flag) {
self.currentIndex = ++self.currentIndex % kImageCount;
} else {
self.currentIndex = --self.currentIndex % kImageCount;
}
return [UIImage imageNamed:[NSString stringWithFormat:@"%ld",self.currentIndex]];
}
代碼十分簡(jiǎn)單潘飘,但是效果和性能卻很驚人肮之。當(dāng)然演示代碼有限,其他動(dòng)畫類型大家可以自己實(shí)現(xiàn)卜录,效果都很絢麗局骤。
2.5逐幀動(dòng)畫
前面介紹了核心動(dòng)畫中大部分動(dòng)畫類型,但是做過(guò)動(dòng)畫處理的朋友都知道暴凑,在動(dòng)畫制作中還有一種動(dòng)畫類型“逐幀動(dòng)畫”。說(shuō)到逐幀動(dòng)畫相信很多朋友第一個(gè)想到的就是UIImageView赘来,通過(guò)設(shè)置UIImageView的animationImages屬性现喳,然后調(diào)用它的startAnimating方法去播放這組圖片凯傲。
當(dāng)然這種方法在某些場(chǎng)景下是可以達(dá)到逐幀的動(dòng)畫效果,但是它也存在著很大的性能問(wèn)題嗦篱,并且這種方法一旦設(shè)置完圖片中間的過(guò)程就無(wú)法控制了冰单。當(dāng)然,也許有朋友會(huì)想到利用iOS的定時(shí)器NSTimer定時(shí)更新圖片來(lái)達(dá)到逐幀動(dòng)畫的效果灸促。這種方式確實(shí)可以解決UIImageView一次性加載大量圖片的問(wèn)題诫欠,而且讓播放過(guò)程可控,唯一的缺點(diǎn)就是定時(shí)器方法調(diào)用有時(shí)可能會(huì)因?yàn)楫?dāng)前系統(tǒng)執(zhí)行某種比較占用時(shí)間的任務(wù)造成動(dòng)畫連續(xù)性出現(xiàn)問(wèn)題浴栽。
雖然在核心動(dòng)畫沒有直接提供逐幀動(dòng)畫類型荒叼,但是卻提供了用于完成逐幀動(dòng)畫的相關(guān)對(duì)象CADisplayLink。CADisplayLink是一個(gè)計(jì)時(shí)器典鸡,但是同NSTimer不同的是被廓,CADisplayLink的刷新周期同屏幕完全一致。例如在iOS中屏幕刷新周期是60次/秒萝玷,CADisplayLink刷新周期同屏幕刷新一致也是60次/秒嫁乘,這樣一來(lái)使用它完成的逐幀動(dòng)畫(又稱為“時(shí)鐘動(dòng)畫”)完全感覺不到動(dòng)畫的停滯情況。
iOS程序在運(yùn)行后就進(jìn)入一個(gè)消息循環(huán)中(這個(gè)消息循環(huán)稱為“主運(yùn)行循環(huán)”)球碉,整個(gè)程序相當(dāng)于進(jìn)入一個(gè)死循環(huán)中蜓斧,始終等待用戶輸入。將CADisplayLink加入到主運(yùn)行循環(huán)隊(duì)列后睁冬,它的時(shí)鐘周期就和主運(yùn)行循環(huán)保持一致挎春,而主運(yùn)行循環(huán)周期就是屏幕刷新周期。在CADisplayLink加入到主運(yùn)行循環(huán)隊(duì)列后就會(huì)循環(huán)調(diào)用目標(biāo)方法痴突,在這個(gè)方法中更新視圖內(nèi)容就可以完成逐幀動(dòng)畫搂蜓。
當(dāng)然這里不得不強(qiáng)調(diào)的是逐幀動(dòng)畫性能勢(shì)必較低,但是對(duì)于一些事物的運(yùn)動(dòng)又不得不選擇使用逐幀動(dòng)畫辽装,例如人的運(yùn)動(dòng)帮碰,這是一個(gè)高度復(fù)雜的運(yùn)動(dòng),基本動(dòng)畫拾积、關(guān)鍵幀動(dòng)畫是不可能解決的殉挽。所大家一定要注意在循環(huán)方法中盡可能的降低算法復(fù)雜度,同時(shí)保證循環(huán)過(guò)程中內(nèi)存峰值盡可能低拓巧。下面以一個(gè)魚的運(yùn)動(dòng)為例為大家演示一下逐幀動(dòng)畫斯碌。
@interface CADisplaylinkAnimationViewController ()
@property (nonatomic, strong) CALayer *fishLayer;
@property (nonatomic, assign) NSInteger index;
@property (nonatomic, strong) NSMutableArray *images;
@end
@implementation CADisplaylinkAnimationViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"逐幀動(dòng)畫";
//創(chuàng)建圖像顯示圖層
self.fishLayer = [[CALayer alloc]init];
self.fishLayer.bounds = CGRectMake(0, 0, 250, 250);
self.fishLayer.position = CGPointMake(160, 150);
self.fishLayer.contents = (id)[UIImage imageNamed:@"timg_0001"].CGImage;
self.fishLayer.backgroundColor = [UIColor redColor].CGColor;
[self.view.layer addSublayer:self.fishLayer];
//由于魚的圖片在循環(huán)中會(huì)不斷創(chuàng)建,而幾張圖片相對(duì)較小
//與其在循環(huán)中不斷創(chuàng)建UIImage不如直接將所有圖片緩存起來(lái)
self.images = [NSMutableArray array];
for (int i = 0; i < 8; i++) {
NSString* imageName = [NSString stringWithFormat:@"timg_000%i.png",i];
UIImage* image = [UIImage imageNamed:imageName];
[self.images addObject:image];
}
//定義時(shí)鐘對(duì)象
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(stepFish)];
//將時(shí)鐘對(duì)象加入到主運(yùn)行循環(huán)RunLoop中
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
UIImageView* animImageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 400, 250, 250 )];
animImageView.animationImages = self.images;
animImageView.animationDuration = 0.8;
[animImageView startAnimating];
[self.view addSubview:animImageView];
}
#pragma mark 每次屏幕刷新就會(huì)執(zhí)行一次此方法(每秒接近60次)
- (void)stepFish {
//定義一個(gè)變量記錄執(zhí)行的次數(shù)
static int s = 0;
//每秒行6次
if (++s % 6 == 0) {
UIImage* image = self.images[self.index];
self.fishLayer.contents = (id)image.CGImage;
self.index = (self.index + 1) % 8;
}
}
@end
3 UIView動(dòng)畫封裝
有了前面核心動(dòng)畫的知識(shí)肛度,相信大家開發(fā)出一般的動(dòng)畫效果應(yīng)該不在話下傻唾。在核心動(dòng)畫開篇也給大家說(shuō)過(guò),其實(shí)UIView本身對(duì)于基本動(dòng)畫和關(guān)鍵幀動(dòng)畫、轉(zhuǎn)場(chǎng)動(dòng)畫都有相應(yīng)的封裝冠骄,在對(duì)動(dòng)畫細(xì)節(jié)沒有特殊要求的情況下使用起來(lái)也要簡(jiǎn)單的多伪煤。可以說(shuō)在日常開發(fā)中90%以上的情況使用UIView的動(dòng)畫封裝方法都可以搞定凛辣,因此在熟悉了核心動(dòng)畫的原理之后還是有必要給大家簡(jiǎn)單介紹一下UIView中各類動(dòng)畫使用方法的抱既。由于前面核心動(dòng)畫內(nèi)容已經(jīng)進(jìn)行過(guò)詳細(xì)介紹,學(xué)習(xí)UIView的封裝方法根本是小菜一碟扁誓,這里對(duì)于一些細(xì)節(jié)就不再贅述了防泵。
3.1 基礎(chǔ)動(dòng)畫
基礎(chǔ)動(dòng)畫部分和前面的基礎(chǔ)動(dòng)畫演示相對(duì)應(yīng),演示點(diǎn)擊屏幕雪花飄落到鼠標(biāo)點(diǎn)擊位置的過(guò)程蝗敢。注意根據(jù)前面介紹的隱式動(dòng)畫知識(shí)其實(shí)非根圖層直接設(shè)置終點(diǎn)位置不需要使用UIView的動(dòng)畫方法也可以實(shí)現(xiàn)動(dòng)畫效果捷泞,因此這里雪花不再放到圖層中而是放到了一個(gè)UIImageView中。下面的代碼演示了通過(guò)block和靜態(tài)方法實(shí)現(xiàn)動(dòng)畫控制的過(guò)程:
@implementation AnimationFromUIViewController {
UIImageView* _imageView;
UIImageView* _ball;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//創(chuàng)建圖像顯示空間
_imageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"snow"]];
_imageView.center = CGPointMake(50, 150);
[self.view addSubview:_imageView];
_ball = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"snowflake"]];
_ball.center = self.view.center;
[self.view addSubview:_ball];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
[self startBasicAnimate:location];
[self startSpringAnimate:location];
}
- (void)startSpringAnimate:(CGPoint)location {
//創(chuàng)建阻尼動(dòng)畫
//damping:阻尼,范圍0-1前普,阻尼越接近于0肚邢,彈性效果越明顯
//velocity:彈性復(fù)位的速度
[UIView animateWithDuration:1.2 delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{
_ball.center = location;
} completion:nil];
}
- (void)startBasicAnimate:(CGPoint)location {
//方法1;block方法
/*
開始動(dòng)畫拭卿,UIView的動(dòng)畫方法執(zhí)行完后動(dòng)畫會(huì)停留在重點(diǎn)位置骡湖,而不需要進(jìn)行任何特殊處理
duration:執(zhí)行時(shí)間
delay:延遲時(shí)間
option:動(dòng)畫設(shè)置,列如自動(dòng)恢復(fù)峻厚,勻速運(yùn)動(dòng)等
completion:動(dòng)畫完成回調(diào)方法
*/
// [UIView animateWithDuration:1.5 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
// _imageView.center = location;
// } completion:^(BOOL finished) {
// NSLog(@"animate is end");
// }];
//方法2:靜態(tài)方法
//開始動(dòng)畫
[UIView beginAnimations:@"ZXBasicAnimation" context:nil];
[UIView setAnimationDuration:1.5];
//[UIView setAnimationDelay:1.0];//設(shè)置延遲
//[UIView setAnimationRepeatAutoreverses:NO];//是否回復(fù)
//[UIView setAnimationRepeatCount:10];//重復(fù)次數(shù)
//[UIView setAnimationStartDate:(NSDate *)];//設(shè)置動(dòng)畫開始運(yùn)行的時(shí)間
//[UIView setAnimationDelegate:self];//設(shè)置代理
//[UIView setAnimationWillStartSelector:(SEL)];//設(shè)置動(dòng)畫開始運(yùn)動(dòng)的執(zhí)行方法
//[UIView setAnimationDidStopSelector:(SEL)];//設(shè)置動(dòng)畫運(yùn)行結(jié)束后的執(zhí)行方法
_imageView.center = location;
//開始動(dòng)畫
[UIView commitAnimations];
}
@end
3.2 彈簧動(dòng)畫效果
由于在iOS開發(fā)中彈性動(dòng)畫使用很普遍响蕴,所以在iOS7蘋果官方直接提供了一個(gè)方法用于彈性動(dòng)畫開發(fā),下面簡(jiǎn)單的演示一下:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//創(chuàng)建圖像顯示控件
_imageView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"ball"]];
_imageView.size = CGSizeMake(100, 100);
_imageView.center=CGPointMake(160, 50);
[self.view addSubview:_imageView];
}
#pragma mark 點(diǎn)擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];
/*創(chuàng)建彈性動(dòng)畫
damping:阻尼惠桃,范圍0-1浦夷,阻尼越接近于0,彈性效果越明顯
velocity:彈性復(fù)位的速度
*/
[UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{
_imageView.center=location; //CGPointMake(160, 284);
} completion:nil];
}
3.3 關(guān)鍵幀動(dòng)畫
從iOS7開始UIView動(dòng)畫中封裝了關(guān)鍵幀動(dòng)畫辜王,下面就來(lái)看一下如何使用UIView封裝方法進(jìn)行關(guān)鍵幀動(dòng)畫控制劈狐,這里實(shí)現(xiàn)前面關(guān)鍵幀動(dòng)畫部分對(duì)于球的控制。
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//關(guān)鍵幀動(dòng)畫呐馆,options
[UIView animateKeyframesWithDuration:5.0 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
//第二關(guān)鍵幀(準(zhǔn)確的說(shuō)第一個(gè)關(guān)鍵幀是開始位置):從0秒開始持續(xù)50%的時(shí)間肥缔,也就是5*0.5 = 2.5秒
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
_imageView.center = CGPointMake(80, 220);
}];
//第三幀,從0.5*5.0秒開始汹来,持續(xù)時(shí)間:5.0*0.25 = 1.25秒
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{
_imageView.center = CGPointMake(40, 300);
}];
//第四幀续膳,從0.75*5.0秒開始,持續(xù)時(shí)間:5.0*0.25秒
[UIView addKeyframeWithRelativeStartTime:0.75f relativeDuration:0.25 animations:^{
_imageView.center = CGPointMake(67, 400);
}];
} completion:^(BOOL finished) {
NSLog(@"animation ended");
}];
/*
options 的補(bǔ)充
UIViewKeyframeAnimationOptionCalculationModeLinear:連續(xù)運(yùn)算模式收班。
UIViewKeyframeAnimationOptionCalculationModeDiscrete :離散運(yùn)算模式坟岔。
UIViewKeyframeAnimationOptionCalculationModePaced:均勻執(zhí)行運(yùn)算模式。
UIViewKeyframeAnimationOptionCalculationModeCubic:平滑運(yùn)算模式摔桦。
UIViewKeyframeAnimationOptionCalculationModeCubicPaced:平滑均勻運(yùn)算模式社付。
*/
}
注意
:前面說(shuō)過(guò)關(guān)鍵幀動(dòng)畫有兩種形式,上面演示的是屬性值關(guān)鍵幀動(dòng)畫,路徑關(guān)鍵幀動(dòng)畫目前UIView還不支持
3.4 轉(zhuǎn)場(chǎng)動(dòng)畫
從iOS4.0開始瘦穆,UIView直接封裝了轉(zhuǎn)場(chǎng)動(dòng)畫纪隙,使用起來(lái)同樣很簡(jiǎn)單。
@implementation TransitionUIAnimationViewController {
UIImageView *_imageView;
int _currentIndex;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//定義圖片控件
_imageView=[[UIImageView alloc]init];
_imageView.frame=[UIScreen mainScreen].applicationFrame;
_imageView.contentMode=UIViewContentModeScaleAspectFit;
_imageView.image=[UIImage imageNamed:@"0.jpg"];//默認(rèn)圖片
[self.view addSubview:_imageView];
//添加手勢(shì)
UISwipeGestureRecognizer *leftSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
leftSwipeGesture.direction=UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:leftSwipeGesture];
UISwipeGestureRecognizer *rightSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
rightSwipeGesture.direction=UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:rightSwipeGesture];
}
#pragma mark 向左滑動(dòng)瀏覽下一張圖片
-(void)leftSwipe:(UISwipeGestureRecognizer *)gesture{
[self transitionAnimation:YES];
}
#pragma mark 向右滑動(dòng)瀏覽上一張圖片
-(void)rightSwipe:(UISwipeGestureRecognizer *)gesture{
[self transitionAnimation:NO];
}
#pragma mark 轉(zhuǎn)場(chǎng)動(dòng)畫
-(void)transitionAnimation:(BOOL)isNext{
UIViewAnimationOptions option;
if (isNext) {
option=UIViewAnimationOptionCurveLinear|UIViewAnimationOptionTransitionFlipFromRight;
} else {
option=UIViewAnimationOptionCurveLinear|UIViewAnimationOptionTransitionFlipFromLeft;
}
[UIView transitionWithView:_imageView duration:1.0 options:option animations:^{
_imageView.image=[self getImage:isNext];
} completion:nil];
}
#pragma mark 取得當(dāng)前圖片
-(UIImage *)getImage:(BOOL)isNext{
if (isNext) {
_currentIndex = ++_currentIndex % IMAGE_COUNT;
} else {
_currentIndex = --_currentIndex % IMAGE_COUNT;
}
NSString *imageName=[NSString stringWithFormat:@"%i.jpg",_currentIndex];
return [UIImage imageNamed:imageName];
}
@end
上面的轉(zhuǎn)場(chǎng)動(dòng)畫演示中扛或,其實(shí)僅僅有一個(gè)視圖UIImageView做轉(zhuǎn)場(chǎng)動(dòng)畫,每次轉(zhuǎn)場(chǎng)通過(guò)切換UIImageView的內(nèi)容而已碘饼。如果有兩個(gè)完全不同的視圖熙兔,并且每個(gè)視圖布局都很復(fù)雜,此時(shí)要在這兩個(gè)視圖之間進(jìn)行轉(zhuǎn)場(chǎng)可以使用+ (void)transitionFromView:(UIView )fromView toView:(UIView )toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(4_0)方法進(jìn)行兩個(gè)視圖間的轉(zhuǎn)場(chǎng)艾恼,需要注意的是默認(rèn)情況下轉(zhuǎn)出的視圖會(huì)從父視圖移除住涉,轉(zhuǎn)入后重新添加,可以通過(guò)UIViewAnimationOptionShowHideTransitionViews參數(shù)設(shè)置钠绍,設(shè)置此參數(shù)后轉(zhuǎn)出的視圖會(huì)隱藏(不會(huì)移除)轉(zhuǎn)入后再顯示舆声。
注意
:轉(zhuǎn)場(chǎng)動(dòng)畫設(shè)置參數(shù)完全同基本動(dòng)畫參數(shù)設(shè)置;同直接使用轉(zhuǎn)場(chǎng)動(dòng)畫不同的是使用UIView的裝飾方法進(jìn)行轉(zhuǎn)場(chǎng)動(dòng)畫其動(dòng)畫效果較少柳爽,因?yàn)檫@里無(wú)法直接使用私有API媳握。
本文參考zmmzxxx的CALayer與iOS動(dòng)畫 講解及使用,非常感謝磷脯。
- 如有錯(cuò)誤蛾找,歡迎指正,多多點(diǎn)贊赵誓,打賞更佳打毛,您的支持是我寫作的動(dòng)力。