大家都知道在iOS中實(shí)現(xiàn)一個(gè)動(dòng)畫(huà)相當(dāng)簡(jiǎn)單,只要調(diào)用UIView的塊代碼即可實(shí)現(xiàn)一個(gè)動(dòng)畫(huà)效果,這在其他系統(tǒng)開(kāi)發(fā)中基本不可能實(shí)現(xiàn)。下面通過(guò)一個(gè)簡(jiǎn)單的UIView進(jìn)行一個(gè)圖片放大動(dòng)畫(huà)效果演示:
#import "KCMainViewController.h"
@interface KCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *image=[UIImage imageNamed:@"open2.png"];
UIImageView *imageView=[[UIImageView alloc]init];
imageView.image=image;
imageView.frame=CGRectMake(120, 140, 80, 80);
[self.view addSubview:imageView];
//兩秒后開(kāi)始一個(gè)持續(xù)一分鐘的動(dòng)畫(huà)
[UIView animateWithDuration:1 delay:2 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
imageView.frame=CGRectMake(80, 100, 160, 160);
} completion:nil];
}
@end
使用上面UIView封裝的方法進(jìn)行動(dòng)畫(huà)設(shè)置固然十分方便,但是具體動(dòng)畫(huà)如何實(shí)現(xiàn)我們是不清楚的,而且上面的代碼還有一些問(wèn)題是無(wú)法解決的,例如:如何控制動(dòng)畫(huà)的暫停?如何進(jìn)行動(dòng)畫(huà)的組合???
這里就需要了解iOS的核心動(dòng)畫(huà)Core Animation(包含在Quartz Core框架中)。在iOS中核心動(dòng)畫(huà)分為幾類(lèi):基礎(chǔ)動(dòng)畫(huà)、關(guān)鍵幀動(dòng)畫(huà)、動(dòng)畫(huà)組、轉(zhuǎn)場(chǎng)動(dòng)畫(huà)。各個(gè)類(lèi)的關(guān)系大致如下:
CAAnimation:核心動(dòng)畫(huà)的基礎(chǔ)類(lèi),不能直接使用,負(fù)責(zé)動(dòng)畫(huà)運(yùn)行時(shí)間、速度的控制,本身實(shí)現(xiàn)了CAMediaTiming協(xié)議。
CAPropertyAnimation:屬性動(dòng)畫(huà)的基類(lèi)(通過(guò)屬性進(jìn)行動(dòng)畫(huà)設(shè)置,注意是可動(dòng)畫(huà)屬性),不能直接使用。
CAAnimationGroup:動(dòng)畫(huà)組,動(dòng)畫(huà)組是一種組合模式設(shè)計(jì),可以通過(guò)動(dòng)畫(huà)組來(lái)進(jìn)行所有動(dòng)畫(huà)行為的統(tǒng)一控制,組中所有動(dòng)畫(huà)效果可以并發(fā)執(zhí)行。
CATransition:轉(zhuǎn)場(chǎng)動(dòng)畫(huà),主要通過(guò)濾鏡進(jìn)行動(dòng)畫(huà)效果設(shè)置。
CABasicAnimation:基礎(chǔ)動(dòng)畫(huà),通過(guò)屬性修改進(jìn)行動(dòng)畫(huà)參數(shù)控制,只有初始狀態(tài)和結(jié)束狀態(tài)。
CAKeyframeAnimation:關(guān)鍵幀動(dòng)畫(huà),同樣是通過(guò)屬性進(jìn)行動(dòng)畫(huà)參數(shù)控制,但是同基礎(chǔ)動(dòng)畫(huà)不同的是它可以有多個(gè)狀態(tài)控制。
基礎(chǔ)動(dòng)畫(huà)、關(guān)鍵幀動(dòng)畫(huà)都屬于屬性動(dòng)畫(huà),就是通過(guò)修改屬性值產(chǎn)生動(dòng)畫(huà)效果,開(kāi)發(fā)人員只需要設(shè)置初始值和結(jié)束值,中間的過(guò)程動(dòng)畫(huà)(又叫“補(bǔ)間動(dòng)畫(huà)”)由系統(tǒng)自動(dòng)計(jì)算產(chǎn)生。和基礎(chǔ)動(dòng)畫(huà)不同的是關(guān)鍵幀動(dòng)畫(huà)可以設(shè)置多個(gè)屬性值,每?jī)蓚€(gè)屬性中間的補(bǔ)間動(dòng)畫(huà)由系統(tǒng)自動(dòng)完成,因此從這個(gè)角度而言基礎(chǔ)動(dòng)畫(huà)又可以看成是有兩個(gè)關(guān)鍵幀的關(guān)鍵幀動(dòng)畫(huà)。
基礎(chǔ)動(dòng)畫(huà)
在開(kāi)發(fā)過(guò)程中很多情況下通過(guò)基礎(chǔ)動(dòng)畫(huà)就可以滿足開(kāi)發(fā)需求,前面例子中使用的UIView代碼塊進(jìn)行圖像放大縮小的演示動(dòng)畫(huà)也是基礎(chǔ)動(dòng)畫(huà)(在iOS7中UIView也對(duì)關(guān)鍵幀動(dòng)畫(huà)進(jìn)行了封裝),只是UIView裝飾方法隱藏了更多的細(xì)節(jié)。如果不使用UIView封裝的方法,動(dòng)畫(huà)創(chuàng)建一般分為以下幾步:
- 初始化動(dòng)畫(huà)并設(shè)置動(dòng)畫(huà)屬性
- 設(shè)置動(dòng)畫(huà)屬性初始值(可以省略)、結(jié)束值以及其他動(dòng)畫(huà)屬性
- 給圖層添加動(dòng)畫(huà)
下面以一個(gè)移動(dòng)動(dòng)畫(huà)為例進(jìn)行演示,在這個(gè)例子中點(diǎn)擊屏幕哪個(gè)位置落花將飛向哪里。
#import "KCMainViewController.h"
@interface KCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
_layer=[CALayer layer];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
}
#pragma mark 移動(dòng)動(dòng)畫(huà)
-(void)translatonAnimation:(CGPoint)location{
//1.創(chuàng)建動(dòng)畫(huà)并指定動(dòng)畫(huà)屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];
//2.設(shè)置動(dòng)畫(huà)屬性初始值和結(jié)束值
// basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設(shè)置,默認(rèn)為圖層初始狀態(tài)
basicAnimation.toValue=[NSValue valueWithCGPoint:location];
//設(shè)置其他動(dòng)畫(huà)屬性
basicAnimation.duration=5.0;//動(dòng)畫(huà)時(shí)間5秒
//basicAnimation.repeatCount=HUGE_VALF;//設(shè)置重復(fù)次數(shù),HUGE_VALF可看做無(wú)窮大,起到循環(huán)動(dòng)畫(huà)的效果
// basicAnimation.removedOnCompletion=NO;//運(yùn)行一次是否移除動(dòng)畫(huà)
//3.添加動(dòng)畫(huà)到圖層,注意key相當(dāng)于給動(dòng)畫(huà)進(jìn)行命名,以后獲得該動(dòng)畫(huà)時(shí)可以使用此名稱(chēng)獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}
#pragma mark 點(diǎn)擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];
//創(chuàng)建并開(kāi)始動(dòng)畫(huà)
[self translatonAnimation:location];
}
@end
運(yùn)行效果:
上面實(shí)現(xiàn)了一個(gè)基本動(dòng)畫(huà)效果,但是這個(gè)動(dòng)畫(huà)存在一個(gè)問(wèn)題:動(dòng)畫(huà)結(jié)束后動(dòng)畫(huà)圖層回到了原來(lái)的位置,當(dāng)然是用UIView封裝的方法是沒(méi)有這個(gè)問(wèn)題的。如何解決這個(gè)問(wèn)題呢?
圖層動(dòng)畫(huà)的本質(zhì)就是將圖層內(nèi)部的內(nèi)容轉(zhuǎn)化為位圖經(jīng)硬件操作形成一種動(dòng)畫(huà)效果,其實(shí)圖層本身并沒(méi)有任何的變化。上面的動(dòng)畫(huà)中圖層并沒(méi)有因?yàn)閯?dòng)畫(huà)效果而改變它的位置(對(duì)于縮放動(dòng)畫(huà)其大小也是不會(huì)改變的),所以動(dòng)畫(huà)完成之后圖層還是在原來(lái)的顯示位置沒(mé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)沒(méi)有變過(guò)。當(dāng)然解決這個(gè)問(wèn)題方法比較多,這里不妨在動(dòng)畫(huà)完成之后重新設(shè)置它的位置。
//
//
#import "KCMainViewController.h"
@interface KCMainViewController ()<CAAnimationDelegate>{
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
}
#pragma mark 移動(dòng)動(dòng)畫(huà)
-(void)translatonAnimation:(CGPoint)location{
//1.創(chuàng)建動(dòng)畫(huà)并指定動(dòng)畫(huà)屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];
//2.設(shè)置動(dòng)畫(huà)屬性初始值和結(jié)束值
// basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設(shè)置,默認(rèn)為圖層初始狀態(tài)
basicAnimation.toValue=[NSValue valueWithCGPoint:location];
//設(shè)置其他動(dòng)畫(huà)屬性
basicAnimation.duration=5.0;//動(dòng)畫(huà)時(shí)間5秒
//basicAnimation.repeatCount=HUGE_VALF;//設(shè)置重復(fù)次數(shù),HUGE_VALF可看做無(wú)窮大,起到循環(huán)動(dòng)畫(huà)的效果
// basicAnimation.removedOnCompletion=NO;//運(yùn)行一次是否移除動(dòng)畫(huà)
basicAnimation.delegate=self;
//存儲(chǔ)當(dāng)前位置在動(dòng)畫(huà)結(jié)束后使用
[basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
//3.添加動(dòng)畫(huà)到圖層,注意key相當(dāng)于給動(dòng)畫(huà)進(jìn)行命名,以后獲得該動(dòng)畫(huà)時(shí)可以使用此名稱(chēng)獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}
#pragma mark 點(diǎn)擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];
//創(chuàng)建并開(kāi)始動(dòng)畫(huà)
[self translatonAnimation:location];
}
#pragma mark - 動(dòng)畫(huà)代理方法
#pragma mark 動(dòng)畫(huà)開(kāi)始
-(void)animationDidStart:(CAAnimation *)anim{
NSLog(@"animation(%@) start.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
NSLog(@"%@",[_layer animationForKey:@"KCBasicAnimation_Translation"]);//通過(guò)前面的設(shè)置的key獲得動(dòng)畫(huà)
}
#pragma mark 動(dòng)畫(huà)結(jié)束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
}
@end
上面通過(guò)給動(dòng)畫(huà)設(shè)置一個(gè)代理去監(jiān)聽(tīng)動(dòng)畫(huà)的開(kāi)始和結(jié)束事件,在動(dòng)畫(huà)開(kāi)始前給動(dòng)畫(huà)添加一個(gè)自定義屬性“KCBasicAnimationLocation”存儲(chǔ)動(dòng)畫(huà)終點(diǎn)位置,然后在動(dòng)畫(huà)結(jié)束后設(shè)置動(dòng)畫(huà)的位置為終點(diǎn)位置。
如果運(yùn)行上面的代碼大家可能會(huì)發(fā)現(xiàn)另外一個(gè)問(wèn)題,那就是動(dòng)畫(huà)運(yùn)行完成后會(huì)重新從起始點(diǎn)運(yùn)動(dòng)到終點(diǎn)。這個(gè)問(wèn)題產(chǎn)生的原因就是前面提到的,對(duì)于非根圖層,設(shè)置圖層的可動(dòng)畫(huà)屬性(在動(dòng)畫(huà)結(jié)束后重新設(shè)置了position,而position是可動(dòng)畫(huà)屬性)會(huì)產(chǎn)生動(dòng)畫(huà)效果。解決這個(gè)問(wèn)題有兩種辦法:關(guān)閉圖層隱式動(dòng)畫(huà)、設(shè)置動(dòng)畫(huà)圖層為根圖層。顯然這里不能采取后者,因?yàn)楦鶊D層當(dāng)前已經(jīng)作為動(dòng)畫(huà)的背景。
要關(guān)閉隱式動(dòng)畫(huà)需要用到動(dòng)畫(huà)事務(wù)CATransaction,在事務(wù)內(nèi)將隱式動(dòng)畫(huà)關(guān)閉,例如上面的代碼可以改為:
#pragma mark 動(dòng)畫(huà)結(jié)束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
//開(kāi)啟事務(wù)
[CATransaction begin];
//禁用隱式動(dòng)畫(huà)
[CATransaction setDisableActions:YES];
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
//提交事務(wù)
[CATransaction commit];
}
當(dāng)然上面的動(dòng)畫(huà)還顯得有些生硬,因?yàn)槁浠h散的時(shí)候可能不僅僅是自由落體運(yùn)動(dòng),本身由于空氣阻力、外界風(fēng)力還會(huì)造成落花在空中的旋轉(zhuǎn)、搖擺等,這里不妨給圖層添加一個(gè)旋轉(zhuǎn)的動(dòng)畫(huà)。對(duì)于圖層的旋轉(zhuǎn)通過(guò)key path設(shè)置圖層旋轉(zhuǎn)的內(nèi)容,在這里需要強(qiáng)調(diào)一下,圖層的形變都是基于錨點(diǎn)進(jìn)行的。例如旋轉(zhuǎn),旋轉(zhuǎn)的中心點(diǎn)就是圖層的錨點(diǎn)。
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.anchorPoint=CGPointMake(0.5, 0.6);//設(shè)置錨點(diǎn)
_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
}
#pragma mark 移動(dòng)動(dòng)畫(huà)
-(void)translatonAnimation:(CGPoint)location{
//1.創(chuàng)建動(dòng)畫(huà)并指定動(dòng)畫(huà)屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];
//2.設(shè)置動(dòng)畫(huà)屬性初始值、結(jié)束值
// basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設(shè)置,默認(rèn)為圖層初始狀態(tài)
basicAnimation.toValue=[NSValue valueWithCGPoint:location];
//設(shè)置其他動(dòng)畫(huà)屬性
basicAnimation.duration=5.0;//動(dòng)畫(huà)時(shí)間5秒
//basicAnimation.repeatCount=HUGE_VALF;//設(shè)置重復(fù)次數(shù),HUGE_VALF可看做無(wú)窮大,起到循環(huán)動(dòng)畫(huà)的效果
// basicAnimation.removedOnCompletion=NO;//運(yùn)行一次是否移除動(dòng)畫(huà)
basicAnimation.delegate=self;
//存儲(chǔ)當(dāng)前位置在動(dòng)畫(huà)結(jié)束后使用
[basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
//3.添加動(dòng)畫(huà)到圖層,注意key相當(dāng)于給動(dòng)畫(huà)進(jìn)行命名,以后獲得該圖層時(shí)可以使用此名稱(chēng)獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}
#pragma mark 旋轉(zhuǎn)動(dòng)畫(huà)
-(void)rotationAnimation{
//1.創(chuàng)建動(dòng)畫(huà)并指定動(dòng)畫(huà)屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//2.設(shè)置動(dòng)畫(huà)屬性初始值、結(jié)束值
// basicAnimation.fromValue=[NSNumber numberWithInt:M_PI_2];
basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];
//設(shè)置其他動(dòng)畫(huà)屬性
basicAnimation.duration=6.0;
basicAnimation.autoreverses=true;//旋轉(zhuǎn)后再旋轉(zhuǎn)到原來(lái)的位置
//4.添加動(dòng)畫(huà)到圖層,注意key相當(dāng)于給動(dòng)畫(huà)進(jìn)行命名,以后獲得該動(dòng)畫(huà)時(shí)可以使用此名稱(chēng)獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}
#pragma mark 點(diǎn)擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];
//創(chuàng)建并開(kāi)始動(dòng)畫(huà)
[self translatonAnimation:location];
[self rotationAnimation];
}
#pragma mark - 動(dòng)畫(huà)代理方法
#pragma mark 動(dòng)畫(huà)開(kāi)始
-(void)animationDidStart:(CAAnimation *)anim{
NSLog(@"animation(%@) start.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
NSLog(@"%@",[_layer animationForKey:@"KCBasicAnimation_Translation"]);//通過(guò)前面的設(shè)置的key獲得動(dòng)畫(huà)
}
#pragma mark 動(dòng)畫(huà)結(jié)束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
//開(kāi)啟事務(wù)
[CATransaction begin];
//禁用隱式動(dòng)畫(huà)
[CATransaction setDisableActions:YES];
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
//提交事務(wù)
[CATransaction commit];
}
@end
上面代碼中結(jié)合兩種動(dòng)畫(huà)操作,需要注意的是只給移動(dòng)動(dòng)畫(huà)設(shè)置了代理,在旋轉(zhuǎn)動(dòng)畫(huà)中并沒(méi)有設(shè)置代理,否則代理方法會(huì)執(zhí)行兩遍。由于旋轉(zhuǎn)動(dòng)畫(huà)會(huì)無(wú)限循環(huán)執(zhí)行(上面設(shè)置了重復(fù)次數(shù)無(wú)窮大),并且兩個(gè)動(dòng)畫(huà)的執(zhí)行時(shí)間沒(méi)有必然的關(guān)系,這樣一來(lái)移動(dòng)停止后可能還在旋轉(zhuǎn),為了讓移動(dòng)動(dòng)畫(huà)停止后旋轉(zhuǎn)動(dòng)畫(huà)停止就需要使用到動(dòng)畫(huà)的暫停和恢復(fù)方法。
核心動(dòng)畫(huà)的運(yùn)行有一個(gè)媒體時(shí)間的概念,假設(shè)將一個(gè)旋轉(zhuǎn)動(dòng)畫(huà)設(shè)置旋轉(zhuǎn)一周用時(shí)60秒的話,那么當(dāng)動(dòng)畫(huà)旋轉(zhuǎn)90度后媒體時(shí)間就是15秒。如果此時(shí)要將動(dòng)畫(huà)暫停只需要讓媒體時(shí)間偏移量設(shè)置為15秒即可,并把動(dòng)畫(huà)運(yùn)行速度設(shè)置為0使其停止運(yùn)動(dòng)。類(lèi)似的,如果又過(guò)了60秒后需要恢復(fù)動(dòng)畫(huà)(此時(shí)媒體時(shí)間為75秒),這時(shí)只要將動(dòng)畫(huà)開(kāi)始開(kāi)始時(shí)間設(shè)置為當(dāng)前媒體時(shí)間75秒減去暫停時(shí)的時(shí)間(也就是之前定格動(dòng)畫(huà)時(shí)的偏移量)15秒(開(kāi)始時(shí)間=75-15=60秒),那么動(dòng)畫(huà)就會(huì)重新計(jì)算60秒后的狀態(tài)再開(kāi)始運(yùn)行,與此同時(shí)將偏移量重新設(shè)置為0并且把運(yùn)行速度設(shè)置1。這個(gè)過(guò)程中真正起到暫停動(dòng)畫(huà)和恢復(fù)動(dòng)畫(huà)的其實(shí)是動(dòng)畫(huà)速度的調(diào)整,媒體時(shí)間偏移量以及恢復(fù)時(shí)的開(kāi)始時(shí)間設(shè)置主要為了讓動(dòng)畫(huà)更加連貫。
下面的代碼演示了移動(dòng)動(dòng)畫(huà)結(jié)束后旋轉(zhuǎn)動(dòng)畫(huà)暫停,并且當(dāng)再次點(diǎn)擊動(dòng)畫(huà)時(shí)旋轉(zhuǎn)恢復(fù)的過(guò)程(注意在移動(dòng)過(guò)程中如果再次點(diǎn)擊屏幕可以暫停移動(dòng)和旋轉(zhuǎn)動(dòng)畫(huà),再次點(diǎn)擊可以恢復(fù)兩種動(dòng)畫(huà)。但是當(dāng)移動(dòng)結(jié)束后觸發(fā)了移動(dòng)動(dòng)畫(huà)的完成事件如果再次點(diǎn)擊屏幕則只能恢復(fù)旋轉(zhuǎn)動(dòng)畫(huà),因?yàn)榇藭r(shí)移動(dòng)動(dòng)畫(huà)已經(jīng)結(jié)束而不是暫停,無(wú)法再恢復(fù))。
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.anchorPoint=CGPointMake(0.5, 0.6);//設(shè)置錨點(diǎn)
_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
}
#pragma mark 移動(dòng)動(dòng)畫(huà)
-(void)translatonAnimation:(CGPoint)location{
//1.創(chuàng)建動(dòng)畫(huà)并指定動(dòng)畫(huà)屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];
//2.設(shè)置動(dòng)畫(huà)屬性初始值、結(jié)束值
// basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設(shè)置,默認(rèn)為圖層初始狀態(tài)
basicAnimation.toValue=[NSValue valueWithCGPoint:location];
//設(shè)置其他動(dòng)畫(huà)屬性
basicAnimation.duration=5.0;//動(dòng)畫(huà)時(shí)間5秒
// basicAnimation.repeatCount=HUGE_VALF;//設(shè)置重復(fù)次數(shù),HUGE_VALF可看做無(wú)窮大,起到循環(huán)動(dòng)畫(huà)的效果
basicAnimation.removedOnCompletion=NO;//運(yùn)行一次是否移除動(dòng)畫(huà)
basicAnimation.delegate=self;
//存儲(chǔ)當(dāng)前位置在動(dòng)畫(huà)結(jié)束后使用
[basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
//3.添加動(dòng)畫(huà)到圖層,注意key相當(dāng)于給動(dòng)畫(huà)進(jìn)行命名,以后獲得該圖層時(shí)可以使用此名稱(chēng)獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}
#pragma mark 旋轉(zhuǎn)動(dòng)畫(huà)
-(void)rotationAnimation{
//1.創(chuàng)建動(dòng)畫(huà)并指定動(dòng)畫(huà)屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//2.設(shè)置動(dòng)畫(huà)屬性初始值、結(jié)束值
// basicAnimation.fromValue=[NSNumber numberWithInt:M_PI_2];
basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];
//設(shè)置其他動(dòng)畫(huà)屬性
basicAnimation.duration=6.0;
basicAnimation.autoreverses=true;//旋轉(zhuǎn)后在旋轉(zhuǎn)到原來(lái)的位置
basicAnimation.repeatCount=HUGE_VALF;//設(shè)置無(wú)限循環(huán)
basicAnimation.removedOnCompletion=NO;
// basicAnimation.delegate=self;
//4.添加動(dòng)畫(huà)到圖層,注意key相當(dāng)于給動(dòng)畫(huà)進(jìn)行命名,以后獲得該動(dòng)畫(huà)時(shí)可以使用此名稱(chēng)獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}
#pragma mark 點(diǎn)擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];
//判斷是否已經(jīng)創(chuàng)建過(guò)動(dòng)畫(huà),如果已經(jīng)創(chuàng)建則不再創(chuàng)建動(dòng)畫(huà)
CAAnimation *animation= [_layer animationForKey:@"KCBasicAnimation_Translation"];
if(animation){
if (_layer.speed==0) {
[self animationResume];
}else{
[self animationPause];
}
}else{
//創(chuàng)建并開(kāi)始動(dòng)畫(huà)
[self translatonAnimation:location];
[self rotationAnimation];
}
}
#pragma mark 動(dòng)畫(huà)暫停
-(void)animationPause{
//取得指定圖層動(dòng)畫(huà)的媒體時(shí)間,后面參數(shù)用于指定子圖層,這里不需要
CFTimeInterval interval=[_layer convertTime:CACurrentMediaTime() fromLayer:nil];
//設(shè)置時(shí)間偏移量,保證暫停時(shí)停留在旋轉(zhuǎn)的位置
[_layer setTimeOffset:interval];
//速度設(shè)置為0,暫停動(dòng)畫(huà)
_layer.speed=0;
}
#pragma mark 動(dòng)畫(huà)恢復(fù)
-(void)animationResume{
//獲得暫停的時(shí)間
CFTimeInterval beginTime= CACurrentMediaTime()- _layer.timeOffset;
//設(shè)置偏移量
_layer.timeOffset=0;
//設(shè)置開(kāi)始時(shí)間
_layer.beginTime=beginTime;
//設(shè)置動(dòng)畫(huà)速度,開(kāi)始運(yùn)動(dòng)
_layer.speed=1.0;
}
#pragma mark - 動(dòng)畫(huà)代理方法
#pragma mark 動(dòng)畫(huà)開(kāi)始
-(void)animationDidStart:(CAAnimation *)anim{
NSLog(@"animation(%@) start.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
NSLog(@"%@",[_layer animationForKey:@"KCBasicAnimation_Translation"]);//通過(guò)前面的設(shè)置的key獲得動(dòng)畫(huà)
}
#pragma mark 動(dòng)畫(huà)結(jié)束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
//開(kāi)啟事務(wù)
[CATransaction begin];
//禁用隱式動(dòng)畫(huà)
[CATransaction setDisableActions:YES];
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
//提交事務(wù)
[CATransaction commit];
//暫停動(dòng)畫(huà)
[self animationPause];
}
@end
運(yùn)行效果:
注意:
動(dòng)畫(huà)暫停針對(duì)的是圖層而不是圖層中的某個(gè)動(dòng)畫(huà)。
要做無(wú)限循環(huán)的動(dòng)畫(huà),動(dòng)畫(huà)的removedOnCompletion屬性必須設(shè)置為NO,否則運(yùn)行一次動(dòng)畫(huà)就會(huì)銷(xiāo)毀。
關(guān)鍵幀動(dòng)畫(huà)
熟悉flash開(kāi)發(fā)的朋友對(duì)于關(guān)鍵幀動(dòng)畫(huà)應(yīng)該不陌生,這種動(dòng)畫(huà)方式在flash開(kāi)發(fā)中經(jīng)常用到。關(guān)鍵幀動(dòng)畫(huà)就是在動(dòng)畫(huà)控制過(guò)程中開(kāi)發(fā)者指定主要的動(dòng)畫(huà)狀態(tài),至于各個(gè)狀態(tài)間動(dòng)畫(huà)如何進(jìn)行則由系統(tǒng)自動(dòng)運(yùn)算補(bǔ)充(每?jī)蓚€(gè)關(guān)鍵幀之間系統(tǒng)形成的動(dòng)畫(huà)稱(chēng)為“補(bǔ)間動(dòng)畫(huà)”),這種動(dòng)畫(huà)的好處就是開(kāi)發(fā)者不用逐個(gè)控制每個(gè)動(dòng)畫(huà)幀,而只要關(guān)心幾個(gè)關(guān)鍵幀的狀態(tài)即可。
關(guān)鍵幀動(dòng)畫(huà)開(kāi)發(fā)分為兩種形式:一種是通過(guò)設(shè)置不同的屬性值進(jìn)行關(guān)鍵幀控制,另一種是通過(guò)繪制路徑進(jìn)行關(guān)鍵幀控制。后者優(yōu)先級(jí)高于前者,如果設(shè)置了路徑則屬性值就不再起作用。
對(duì)于前面的落花動(dòng)畫(huà)效果而言其實(shí)落花的過(guò)程并不自然,很顯然實(shí)際生活中它不可能沿著直線下落,這里我們不妨通過(guò)關(guān)鍵幀動(dòng)畫(huà)的values屬性控制它在下落過(guò)程中的屬性。假設(shè)下落過(guò)程如圖:
在這里需要設(shè)置四個(gè)關(guān)鍵幀(如圖中四個(gè)關(guān)鍵點(diǎn)),具體代碼如下(動(dòng)畫(huà)創(chuàng)建過(guò)程同基本動(dòng)畫(huà)基本完全一致):
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
//創(chuàng)建動(dòng)畫(huà)
[self translationAnimation];
}
#pragma mark 關(guān)鍵幀動(dòng)畫(huà)
-(void)translationAnimation{
//1.創(chuàng)建關(guān)鍵幀動(dòng)畫(huà)并設(shè)置動(dòng)畫(huà)屬性
CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
//2.設(shè)置關(guān)鍵幀,這里有四個(gè)關(guān)鍵幀
NSValue *key1=[NSValue valueWithCGPoint:_layer.position];//對(duì)于關(guān)鍵幀動(dòng)畫(huà)初始值不能省略
NSValue *key2=[NSValue valueWithCGPoint:CGPointMake(80, 220)];
NSValue *key3=[NSValue valueWithCGPoint:CGPointMake(45, 300)];
NSValue *key4=[NSValue valueWithCGPoint:CGPointMake(55, 400)];
NSArray *values=@[key1,key2,key3,key4];
keyframeAnimation.values=values;
//設(shè)置其他屬性
keyframeAnimation.duration=8.0;
keyframeAnimation.beginTime=CACurrentMediaTime()+2;//設(shè)置延遲2秒執(zhí)行
//3.添加動(dòng)畫(huà)到圖層,添加動(dòng)畫(huà)后就會(huì)執(zhí)行動(dòng)畫(huà)
[_layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
}
@end
運(yùn)行效果(注意運(yùn)行結(jié)束沒(méi)有設(shè)置圖層位置為動(dòng)畫(huà)運(yùn)動(dòng)結(jié)束位置):
上面的方式固然比前面使用基礎(chǔ)動(dòng)畫(huà)效果要好一些,但其實(shí)還是存在問(wèn)題,那就是落花飛落的路徑是直線的,當(dāng)然這個(gè)直線是根據(jù)程序中設(shè)置的四個(gè)關(guān)鍵幀自動(dòng)形成的,那么如何讓它沿著曲線飄落呢?這就是第二種類(lèi)型的關(guān)鍵幀動(dòng)畫(huà),通過(guò)描繪路徑進(jìn)行關(guān)鍵幀動(dòng)畫(huà)控制。假設(shè)讓落花沿著下面的曲線路徑飄落:
當(dāng)然,這是一條貝塞爾曲線,學(xué)習(xí)了前篇文章之后相信對(duì)于這類(lèi)曲線應(yīng)該并不陌生,下面是具體實(shí)現(xiàn)代碼:
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
//創(chuàng)建動(dòng)畫(huà)
[self translationAnimation];
}
#pragma mark 關(guān)鍵幀動(dòng)畫(huà)
-(void)translationAnimation{
//1.創(chuàng)建關(guān)鍵幀動(dòng)畫(huà)并設(shè)置動(dòng)畫(huà)屬性
CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
//2.設(shè)置路徑
//繪制貝塞爾曲線
CGPathRef path=CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, _layer.position.x, _layer.position.y);//移動(dòng)到起始點(diǎn)
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, 55, 400);//繪制二次貝塞爾曲線
keyframeAnimation.path=path;//設(shè)置path屬性
CGPathRelease(path);//釋放路徑對(duì)象
//設(shè)置其他屬性
keyframeAnimation.duration=8.0;
keyframeAnimation.beginTime=CACurrentMediaTime()+5;//設(shè)置延遲2秒執(zhí)行
//3.添加動(dòng)畫(huà)到圖層,添加動(dòng)畫(huà)后就會(huì)執(zhí)行動(dòng)畫(huà)
[_layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
}
@end
運(yùn)行效果(注意運(yùn)行結(jié)束沒(méi)有設(shè)置圖層位置為動(dòng)畫(huà)運(yùn)動(dòng)結(jié)束位置):
看起來(lái)動(dòng)畫(huà)不會(huì)那么生硬了,但是這里需要注意,對(duì)于路徑類(lèi)型的關(guān)鍵幀動(dòng)畫(huà)系統(tǒng)是從描繪路徑的位置開(kāi)始路徑,直到路徑結(jié)束。如果上面的路徑不是貝塞爾曲線而是矩形路徑那么它會(huì)從矩形的左上角開(kāi)始運(yùn)行,順時(shí)針一周回到左上角;如果指定的路徑是一個(gè)橢圓,那么動(dòng)畫(huà)運(yùn)行的路徑是從橢圓右側(cè)開(kāi)始(0度)順時(shí)針一周回到右側(cè)。
補(bǔ)充--其他屬性
在關(guān)鍵幀動(dòng)畫(huà)中還有一些動(dòng)畫(huà)屬性初學(xué)者往往比較容易混淆,這里專(zhuān)門(mén)針對(duì)這些屬性做一下介紹。
keyTimes:各個(gè)關(guān)鍵幀的時(shí)間控制。前面使用values設(shè)置了四個(gè)關(guān)鍵幀,默認(rèn)情況下每?jī)蓭g的間隔為:8/(4-1)秒。如果想要控制動(dòng)畫(huà)從第一幀到第二針占用時(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)畫(huà)計(jì)算模式。還拿上面keyValues動(dòng)畫(huà)舉例,之所以1到2幀能形成連貫性動(dòng)畫(huà)而不是直接從第1幀經(jīng)過(guò)8/3秒到第2幀是因?yàn)閯?dòng)畫(huà)模式是連續(xù)的(值為kCAAnimationLinear,這是計(jì)算模式的默認(rèn)值);而如果指定了動(dòng)畫(huà)模式為kCAAnimationDiscrete離散的那么你會(huì)看到動(dòng)畫(huà)從第1幀經(jīng)過(guò)8/3秒直接到第2幀,中間沒(méi)有任何過(guò)渡。其他動(dòng)畫(huà)模式還有:kCAAnimationPaced(均勻執(zhí)行,會(huì)忽略keyTimes)、kCAAnimationCubic(平滑執(zhí)行,對(duì)于位置變動(dòng)關(guān)鍵幀動(dòng)畫(huà)運(yùn)行軌跡更平滑)、kCAAnimationCubicPaced(平滑均勻執(zhí)行)。
下圖描繪出了幾種動(dòng)畫(huà)模式的關(guān)系(橫坐標(biāo)是運(yùn)行時(shí)間,縱坐標(biāo)是動(dòng)畫(huà)屬性[例如位置、透明度等]):
動(dòng)畫(huà)組
實(shí)際開(kāi)發(fā)中一個(gè)物體的運(yùn)動(dòng)往往是復(fù)合運(yùn)動(dòng),單一屬性的運(yùn)動(dòng)情況比較少,但恰恰屬性動(dòng)畫(huà)每次進(jìn)行動(dòng)畫(huà)設(shè)置時(shí)一次只能設(shè)置一個(gè)屬性進(jìn)行動(dòng)畫(huà)控制(不管是基礎(chǔ)動(dòng)畫(huà)還是關(guān)鍵幀動(dòng)畫(huà)都是如此),這樣一來(lái)要做一個(gè)復(fù)合運(yùn)動(dòng)的動(dòng)畫(huà)就必須創(chuàng)建多個(gè)屬性動(dòng)畫(huà)進(jìn)行組合。對(duì)于一兩種動(dòng)畫(huà)的組合或許處理起來(lái)還比較容易,但是對(duì)于更多動(dòng)畫(huà)的組合控制往往會(huì)變得很麻煩,動(dòng)畫(huà)組的產(chǎn)生就是基于這樣一種情況而產(chǎn)生的。動(dòng)畫(huà)組是一系列動(dòng)畫(huà)的組合,凡是添加到動(dòng)畫(huà)組中的動(dòng)畫(huà)都受控于動(dòng)畫(huà)組,這樣一來(lái)各類(lèi)動(dòng)畫(huà)公共的行為就可以統(tǒng)一進(jìn)行控制而不必單獨(dú)設(shè)置,而且放到動(dòng)畫(huà)組中的各個(gè)動(dòng)畫(huà)可以并發(fā)執(zhí)行,共同構(gòu)建出復(fù)雜的動(dòng)畫(huà)效果。
動(dòng)畫(huà)組使用起來(lái)并不復(fù)雜,首先單獨(dú)創(chuàng)建單個(gè)動(dòng)畫(huà)(可以是基礎(chǔ)動(dòng)畫(huà)也可以是關(guān)鍵幀動(dòng)畫(huà)),然后將基礎(chǔ)動(dòng)畫(huà)添加到動(dòng)畫(huà)組,最后將動(dòng)畫(huà)組添加到圖層即可。
前面關(guān)鍵幀動(dòng)畫(huà)部分,路徑動(dòng)畫(huà)看起來(lái)效果雖然很流暢,但是落花本身的旋轉(zhuǎn)運(yùn)動(dòng)沒(méi)有了,這里不妨將基礎(chǔ)動(dòng)畫(huà)部分的旋轉(zhuǎn)動(dòng)畫(huà)和路徑關(guān)鍵幀動(dòng)畫(huà)進(jìn)行組合使得整個(gè)動(dòng)畫(huà)看起來(lái)更加的和諧、順暢。
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景(注意這個(gè)圖片其實(shí)在根圖層)
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//自定義一個(gè)圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
//創(chuàng)建動(dòng)畫(huà)
[self groupAnimation];
}
#pragma mark 基礎(chǔ)旋轉(zhuǎn)動(dòng)畫(huà)
-(CABasicAnimation *)rotationAnimation{
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
CGFloat toValue=M_PI_2*3;
basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];
// basicAnimation.duration=6.0;
basicAnimation.autoreverses=true;
basicAnimation.repeatCount=HUGE_VALF;
basicAnimation.removedOnCompletion=NO;
[basicAnimation setValue:[NSNumber numberWithFloat:toValue] forKey:@"KCBasicAnimationProperty_ToValue"];
return basicAnimation;
}
#pragma mark 關(guān)鍵幀移動(dòng)動(dòng)畫(huà)
-(CAKeyframeAnimation *)translationAnimation{
CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
CGPoint endPoint= CGPointMake(55, 400);
CGPathRef path=CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, _layer.position.x, _layer.position.y);
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, endPoint.x, endPoint.y);
keyframeAnimation.path=path;
CGPathRelease(path);
[keyframeAnimation setValue:[NSValue valueWithCGPoint:endPoint] forKey:@"KCKeyframeAnimationProperty_EndPosition"];
return keyframeAnimation;
}
#pragma mark 創(chuàng)建動(dòng)畫(huà)組
-(void)groupAnimation{
//1.創(chuàng)建動(dòng)畫(huà)組
CAAnimationGroup *animationGroup=[CAAnimationGroup animation];
//2.設(shè)置組中的動(dòng)畫(huà)和其他屬性
CABasicAnimation *basicAnimation=[self rotationAnimation];
CAKeyframeAnimation *keyframeAnimation=[self translationAnimation];
animationGroup.animations=@[basicAnimation,keyframeAnimation];
animationGroup.delegate=self;
animationGroup.duration=10.0;//設(shè)置動(dòng)畫(huà)時(shí)間,如果動(dòng)畫(huà)組中動(dòng)畫(huà)已經(jīng)設(shè)置過(guò)動(dòng)畫(huà)屬性則不再生效
animationGroup.beginTime=CACurrentMediaTime()+5;//延遲五秒執(zhí)行
//3.給圖層添加動(dòng)畫(huà)
[_layer addAnimation:animationGroup forKey:nil];
}
#pragma mark - 代理方法
#pragma mark 動(dòng)畫(huà)完成
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
CAAnimationGroup *animationGroup=(CAAnimationGroup *)anim;
CABasicAnimation *basicAnimation=animationGroup.animations[0];
CAKeyframeAnimation *keyframeAnimation=animationGroup.animations[1];
CGFloat toValue=[[basicAnimation valueForKey:@"KCBasicAnimationProperty_ToValue"] floatValue];
CGPoint endPoint=[[keyframeAnimation valueForKey:@"KCKeyframeAnimationProperty_EndPosition"] CGPointValue];
[CATransaction begin];
[CATransaction setDisableActions:YES];
//設(shè)置動(dòng)畫(huà)最終狀態(tài)
_layer.position=endPoint;
_layer.transform=CATransform3DMakeRotation(toValue, 0, 0, 1);
[CATransaction commit];
}
@end
運(yùn)行效果:
轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
轉(zhuǎn)場(chǎng)動(dòng)畫(huà)就是從一個(gè)場(chǎng)景以動(dòng)畫(huà)的形式過(guò)渡到另一個(gè)場(chǎng)景。轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的使用一般分為以下幾個(gè)步驟:
創(chuàng)建轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
設(shè)置轉(zhuǎn)場(chǎng)類(lèi)型、子類(lèi)型(可選)及其他屬性
設(shè)置轉(zhuǎn)場(chǎng)后的新視圖并添加動(dòng)畫(huà)到圖層
下表列出了常用的轉(zhuǎn)場(chǎng)類(lèi)型(注意私有API是蘋(píng)果官方?jīng)]有公開(kāi)的動(dòng)畫(huà)類(lèi)型,但是目前通過(guò)仍然可以使用):
動(dòng)畫(huà)類(lèi)型 | 說(shuō)明 | 對(duì)應(yīng)常量 | 是否支持方向設(shè)置 |
---|---|---|---|
公開(kāi)API | |||
fade | 淡出效果 | kCATransitionFade | 是 |
movein | 新視圖移動(dòng)到舊視圖上 | kCATransitionMoveIn | 是 |
push | 新視圖推出舊視圖 | kCATransitionPush | 是 |
reveal | 移開(kāi)舊視圖顯示新視圖 | kCATransitionReveal | 是 |
私有API | 私有API只能通過(guò)字符串訪問(wèn) | ||
cube | 立方體翻轉(zhuǎn)效果 | 無(wú) | 是 |
oglFlip | 翻轉(zhuǎn)效果 | 無(wú) | 是 |
suckEffect | 收縮效果 | 無(wú) | 否 |
rippleEffect | 水滴波紋效果 | 無(wú) | 否 |
pageCurl | 向上翻頁(yè)效果 | 無(wú) | 是 |
pageUnCurl | 向下翻頁(yè)效果 | 無(wú) | 是 |
cameralIrisHollowOpen | 攝像頭打開(kāi)效果 | 無(wú) | 否 |
cameraIrisHollowClose | 攝像頭關(guān)閉效果 | 無(wú) | 否 |
另外對(duì)于支持方向設(shè)置的動(dòng)畫(huà)類(lèi)型還包含子類(lèi)型:
動(dòng)畫(huà)子類(lèi)型 | 說(shuō)明 |
---|---|
kCATransitionFromRight | 從右側(cè)轉(zhuǎn)場(chǎng) |
kCATransitionFromLeft | 從左側(cè)轉(zhuǎn)場(chǎng) |
kCATransitionFromTop | 從頂部轉(zhuǎn)場(chǎng) |
kCATransitionFromBottom | 從底部轉(zhuǎn)場(chǎng) |
使用轉(zhuǎn)場(chǎng)動(dòng)畫(huà)利用一個(gè)UIImageView實(shí)現(xiàn)一個(gè)漂亮的無(wú)限循環(huán)圖片瀏覽器:
//
#import "KCMainViewController.h"
#define IMAGE_COUNT 5
@interface KCMainViewController (){
UIImageView *_imageView;
int _currentIndex;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//定義圖片控件
_imageView=[[UIImageView alloc]init];
_imageView.frame=[UIScreen mainScreen].bounds;
_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)畫(huà)
-(void)transitionAnimation:(BOOL)isNext{
//1.創(chuàng)建轉(zhuǎn)場(chǎng)動(dòng)畫(huà)對(duì)象
CATransition *transition=[[CATransition alloc]init];
//2.設(shè)置動(dòng)畫(huà)類(lèi)型,注意對(duì)于蘋(píng)果官方?jīng)]公開(kāi)的動(dòng)畫(huà)類(lèi)型只能使用字符串,并沒(méi)有對(duì)應(yīng)的常量定義
transition.type=@"cube";
//設(shè)置子類(lèi)型
if (isNext) {
transition.subtype=kCATransitionFromRight;
}else{
transition.subtype=kCATransitionFromLeft;
}
//設(shè)置動(dòng)畫(huà)時(shí)常
transition.duration=1.0f;
//3.設(shè)置轉(zhuǎn)場(chǎng)后的新視圖添加轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
_imageView.image=[self getImage:isNext];
[_imageView.layer addAnimation:transition forKey:@"KCTransitionAnimation"];
}
#pragma mark 取得當(dāng)前圖片
-(UIImage *)getImage:(BOOL)isNext{
if (isNext) {
_currentIndex=(_currentIndex+1)%IMAGE_COUNT;
}else{
_currentIndex=(_currentIndex-1+IMAGE_COUNT)%IMAGE_COUNT;
}
NSString *imageName=[NSString stringWithFormat:@"%i.jpg",_currentIndex];
return [UIImage imageNamed:imageName];
}
@end
運(yùn)行效果:
代碼十分簡(jiǎn)單,但是效果和性能卻很驚人。當(dāng)然演示代碼有限,其他動(dòng)畫(huà)類(lèi)型大家可以自己實(shí)現(xiàn),效果都很絢麗。
逐幀動(dòng)畫(huà)
前面介紹了核心動(dòng)畫(huà)中大部分動(dòng)畫(huà)類(lèi)型,但是做過(guò)動(dòng)畫(huà)處理的朋友都知道,在動(dòng)畫(huà)制作中還有一種動(dòng)畫(huà)類(lèi)型“逐幀動(dòng)畫(huà)”。說(shuō)到逐幀動(dòng)畫(huà)相信很多朋友第一個(gè)想到的就是UIImageView,通過(guò)設(shè)置UIImageView的animationImages屬性,然后調(diào)用它的startAnimating方法去播放這組圖片。當(dāng)然這種方法在某些場(chǎng)景下是可以達(dá)到逐幀的動(dòng)畫(huà)效果,但是它也存在著很大的性能問(wèn)題,并且這種方法一旦設(shè)置完圖片中間的過(guò)程就無(wú)法控制了。當(dāng)然,也許有朋友會(huì)想到利用iOS的定時(shí)器NSTimer定時(shí)更新圖片來(lái)達(dá)到逐幀動(dòng)畫(huà)的效果。這種方式確實(shí)可以解決UIImageView一次性加載大量圖片的問(wèn)題,而且讓播放過(guò)程可控,唯一的缺點(diǎn)就是定時(shí)器方法調(diào)用有時(shí)可能會(huì)因?yàn)楫?dāng)前系統(tǒng)執(zhí)行某種比較占用時(shí)間的任務(wù)造成動(dòng)畫(huà)連續(xù)性出現(xiàn)問(wèn)題。
雖然在核心動(dòng)畫(huà)沒(méi)有直接提供逐幀動(dòng)畫(huà)類(lèi)型,但是卻提供了用于完成逐幀動(dòng)畫(huà)的相關(guān)對(duì)象CADisplayLink。CADisplayLink是一個(gè)計(jì)時(shí)器,但是同NSTimer不同的是,CADisplayLink的刷新周期同屏幕完全一致。例如在iOS中屏幕刷新周期是60次/秒,CADisplayLink刷新周期同屏幕刷新一致也是60次/秒,這樣一來(lái)使用它完成的逐幀動(dòng)畫(huà)(又稱(chēng)為“時(shí)鐘動(dòng)畫(huà)”)完全感覺(jué)不到動(dòng)畫(huà)的停滯情況。
iOS程序在運(yùn)行后就進(jìn)入一個(gè)消息循環(huán)中(這個(gè)消息循環(huán)稱(chēng)為“主運(yùn)行循環(huán)”),整個(gè)程序相當(dāng)于進(jìn)入一個(gè)死循環(huán)中,始終等待用戶(hù)輸入。將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)畫(huà)。
當(dāng)然這里不得不強(qiáng)調(diào)的是逐幀動(dòng)畫(huà)性能勢(shì)必較低,但是對(duì)于一些事物的運(yùn)動(dòng)又不得不選擇使用逐幀動(dòng)畫(huà),例如人的運(yùn)動(dòng),這是一個(gè)高度復(fù)雜的運(yùn)動(dòng),基本動(dòng)畫(huà)、關(guān)鍵幀動(dòng)畫(huà)是不可能解決的。所大家一定要注意在循環(huán)方法中盡可能的降低算法復(fù)雜度,同時(shí)保證循環(huán)過(guò)程中內(nèi)存峰值盡可能低。下面以一個(gè)魚(yú)的運(yùn)動(dòng)為例為大家演示一下逐幀動(dòng)畫(huà)。
//
#import "KCMainViewController.h"
#define IMAGE_COUNT 10
@interface KCMainViewController (){
CALayer *_layer;
int _index;
NSMutableArray *_images;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景
self.view.layer.contents=(id)[UIImage imageNamed:@"bg.png"].CGImage;
//創(chuàng)建圖像顯示圖層
_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 87, 32);
_layer.position=CGPointMake(160, 284);
[self.view.layer addSublayer:_layer];
//由于魚(yú)的圖片在循環(huán)中會(huì)不斷創(chuàng)建,而10張魚(yú)的照片相對(duì)都很小
//與其在循環(huán)中不斷創(chuàng)建UIImage不如直接將10張圖片緩存起來(lái)
_images=[NSMutableArray array];
for (int i=0; i<10; ++i) {
NSString *imageName=[NSString stringWithFormat:@"fish%i.png",i];
UIImage *image=[UIImage imageNamed:imageName];
[_images addObject:image];
}
//定義時(shí)鐘對(duì)象
CADisplayLink *displayLink=[CADisplayLink displayLinkWithTarget:self selector:@selector(step)];
//添加時(shí)鐘對(duì)象到主運(yùn)行循環(huán)
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
#pragma mark 每次屏幕刷新就會(huì)執(zhí)行一次此方法(每秒接近60次)
-(void)step{
//定義一個(gè)變量記錄執(zhí)行次數(shù)
static int s=0;
//每秒執(zhí)行6次
if (++s%10==0) {
UIImage *image=_images[_index];
_layer.contents=(id)image.CGImage;//更新圖片
_index=(_index+1)%IMAGE_COUNT;
}
}
@end
運(yùn)行效果:
注意:上面僅僅演示了逐幀動(dòng)畫(huà)的過(guò)程,事實(shí)上結(jié)合其他動(dòng)畫(huà)類(lèi)型可以讓整條魚(yú)游動(dòng)起來(lái),這里不再贅述。
UIView動(dòng)畫(huà)封裝
有了前面核心動(dòng)畫(huà)的知識(shí),相信大家開(kāi)發(fā)出一般的動(dòng)畫(huà)效果應(yīng)該不在話下。在核心動(dòng)畫(huà)開(kāi)篇也給大家說(shuō)過(guò),其實(shí)UIView本身對(duì)于基本動(dòng)畫(huà)和關(guān)鍵幀動(dòng)畫(huà)、轉(zhuǎn)場(chǎng)動(dòng)畫(huà)都有相應(yīng)的封裝,在對(duì)動(dòng)畫(huà)細(xì)節(jié)沒(méi)有特殊要求的情況下使用起來(lái)也要簡(jiǎn)單的多。可以說(shuō)在日常開(kāi)發(fā)中90%以上的情況使用UIView的動(dòng)畫(huà)封裝方法都可以搞定,因此在熟悉了核心動(dòng)畫(huà)的原理之后還是有必要給大家簡(jiǎn)單介紹一下UIView中各類(lèi)動(dòng)畫(huà)使用方法的。由于前面核心動(dòng)畫(huà)內(nèi)容已經(jīng)進(jìn)行過(guò)詳細(xì)介紹,學(xué)習(xí)UIView的封裝方法根本是小菜一碟,這里對(duì)于一些細(xì)節(jié)就不再贅述了。
基礎(chǔ)動(dòng)畫(huà)
基礎(chǔ)動(dòng)畫(huà)部分和前面的基礎(chǔ)動(dòng)畫(huà)演示相對(duì)應(yīng),演示點(diǎn)擊屏幕落葉飄落到鼠標(biāo)點(diǎn)擊位置的過(guò)程。注意根據(jù)前面介紹的隱式動(dòng)畫(huà)知識(shí)其實(shí)非根圖層直接設(shè)置終點(diǎn)位置不需要使用UIView的動(dòng)畫(huà)方法也可以實(shí)現(xiàn)動(dòng)畫(huà)效果,因此這里落花不再放到圖層中而是放到了一個(gè)UIImageView中。
下面的代碼演示了通過(guò)block和靜態(tài)方法實(shí)現(xiàn)動(dòng)畫(huà)控制的過(guò)程:
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
UIImageView *_imageView;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//創(chuàng)建圖像顯示控件
_imageView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"petal.png"]];
_imageView.center=CGPointMake(50, 150);
[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];
//方法1:block方式
/*開(kāi)始動(dòng)畫(huà),UIView的動(dòng)畫(huà)方法執(zhí)行完后動(dòng)畫(huà)會(huì)停留在重點(diǎn)位置,而不需要進(jìn)行任何特殊處理
duration:執(zhí)行時(shí)間
delay:延遲時(shí)間
options:動(dòng)畫(huà)設(shè)置,例如自動(dòng)恢復(fù)、勻速運(yùn)動(dòng)等
completion:動(dòng)畫(huà)完成回調(diào)方法
*/
// [UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
// _imageView.center=location;
// } completion:^(BOOL finished) {
// NSLog(@"Animation end.");
// }];
//方法2:靜態(tài)方法,較方法1好些
//開(kāi)始動(dòng)畫(huà)
[UIView beginAnimations:@"KCBasicAnimation" context:nil];
[UIView setAnimationDuration:5.0];
//[UIView setAnimationDelay:1.0];//設(shè)置延遲
//[UIView setAnimationRepeatAutoreverses:NO];//是否回復(fù)
//[UIView setAnimationRepeatCount:10];//重復(fù)次數(shù)
//[UIView setAnimationStartDate:(NSDate *)];//設(shè)置動(dòng)畫(huà)開(kāi)始運(yùn)行的時(shí)間
//[UIView setAnimationDelegate:self];//設(shè)置代理
//[UIView setAnimationWillStartSelector:(SEL)];//設(shè)置動(dòng)畫(huà)開(kāi)始運(yùn)動(dòng)的執(zhí)行方法
//[UIView setAnimationDidStopSelector:(SEL)];//設(shè)置動(dòng)畫(huà)運(yùn)行結(jié)束后的執(zhí)行方法
_imageView.center=location;
//開(kāi)始動(dòng)畫(huà)
[UIView commitAnimations];
}
@end
補(bǔ)充--彈簧動(dòng)畫(huà)效果
由于在iOS開(kāi)發(fā)中彈性動(dòng)畫(huà)使用很普遍,所以在iOS7蘋(píng)果官方直接提供了一個(gè)方法用于彈性動(dòng)畫(huà)開(kāi)發(fā),下面簡(jiǎn)單的演示一下:
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
UIImageView *_imageView;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建圖像顯示控件
_imageView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"ball.png"]];
_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)畫(huà)
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];
}
@end
運(yùn)行效果:
補(bǔ)充--動(dòng)畫(huà)設(shè)置參數(shù)
在動(dòng)畫(huà)方法中有一個(gè)option參數(shù),UIViewAnimationOptions類(lèi)型,它是一個(gè)枚舉類(lèi)型,動(dòng)畫(huà)參數(shù)分為三類(lèi),可以組合使用:
- 常規(guī)動(dòng)畫(huà)屬性設(shè)置(可以同時(shí)選擇多個(gè)進(jìn)行設(shè)置)
UIViewAnimationOptionLayoutSubviews:動(dòng)畫(huà)過(guò)程中保證子視圖跟隨運(yùn)動(dòng)。
UIViewAnimationOptionAllowUserInteraction:動(dòng)畫(huà)過(guò)程中允許用戶(hù)交互。
UIViewAnimationOptionBeginFromCurrentState:所有視圖從當(dāng)前狀態(tài)開(kāi)始運(yùn)行。
UIViewAnimationOptionRepeat:重復(fù)運(yùn)行動(dòng)畫(huà)。
UIViewAnimationOptionAutoreverse :動(dòng)畫(huà)運(yùn)行到結(jié)束點(diǎn)后仍然以動(dòng)畫(huà)方式回到初始點(diǎn)。
UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套動(dòng)畫(huà)時(shí)間設(shè)置。
UIViewAnimationOptionOverrideInheritedCurve:忽略嵌套動(dòng)畫(huà)速度設(shè)置。
UIViewAnimationOptionAllowAnimatedContent:動(dòng)畫(huà)過(guò)程中重繪視圖(注意僅僅適用于轉(zhuǎn)場(chǎng)動(dòng)畫(huà))。
UIViewAnimationOptionShowHideTransitionViews:視圖切換時(shí)直接隱藏舊視圖、顯示新視圖,而不是將舊視圖從父視圖移除(僅僅適用于轉(zhuǎn)場(chǎng)動(dòng)畫(huà))
UIViewAnimationOptionOverrideInheritedOptions :不繼承父動(dòng)畫(huà)設(shè)置或動(dòng)畫(huà)類(lèi)型。
- 動(dòng)畫(huà)速度控制(可從其中選擇一個(gè)設(shè)置)
UIViewAnimationOptionCurveEaseInOut:動(dòng)畫(huà)開(kāi)始緩慢,中間快,結(jié)束慢。
UIViewAnimationOptionCurveEaseIn :動(dòng)畫(huà)開(kāi)始慢,之后逐漸加速。
UIViewAnimationOptionCurveEaseOut:動(dòng)畫(huà)開(kāi)始快,之后逐漸變慢。
UIViewAnimationOptionCurveLinear :動(dòng)畫(huà)勻速執(zhí)行,默認(rèn)值。
- 轉(zhuǎn)場(chǎng)類(lèi)型(僅適用于轉(zhuǎn)場(chǎng)動(dòng)畫(huà)設(shè)置,可以從中選擇一個(gè)進(jìn)行設(shè)置,基本動(dòng)畫(huà)、關(guān)鍵幀動(dòng)畫(huà)不需要設(shè)置)
UIViewAnimationOptionTransitionNone:沒(méi)有轉(zhuǎn)場(chǎng)動(dòng)畫(huà)效果。
UIViewAnimationOptionTransitionFlipFromLeft :從左側(cè)翻轉(zhuǎn)效果。
UIViewAnimationOptionTransitionFlipFromRight:從右側(cè)翻轉(zhuǎn)效果。
UIViewAnimationOptionTransitionCurlUp:向后翻頁(yè)的動(dòng)畫(huà)過(guò)渡效果。
UIViewAnimationOptionTransitionCurlDown :向前翻頁(yè)的動(dòng)畫(huà)過(guò)渡效果。
UIViewAnimationOptionTransitionCrossDissolve:舊視圖溶解消失顯示下一個(gè)新視圖的效果。
UIViewAnimationOptionTransitionFlipFromTop :從上方翻轉(zhuǎn)效果。
UIViewAnimationOptionTransitionFlipFromBottom:從底部翻轉(zhuǎn)效果。
關(guān)鍵幀動(dòng)畫(huà)
從iOS7開(kāi)始UIView動(dòng)畫(huà)中封裝了關(guān)鍵幀動(dòng)畫(huà),下面就來(lái)看一下如何使用UIView封裝方法進(jìn)行關(guān)鍵幀動(dòng)畫(huà)控制,這里實(shí)現(xiàn)前面關(guān)鍵幀動(dòng)畫(huà)部分對(duì)于落花的控制。
//
#import "KCMainViewController.h"
@interface KCMainViewController (){
UIImageView *_imageView;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//設(shè)置背景
UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];
//創(chuàng)建圖像顯示控件
_imageView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"petal.png"]];
_imageView.center=CGPointMake(50, 150);
[self.view addSubview:_imageView];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//UITouch *touch=touches.anyObject;
//CGPoint location= [touch locationInView:self.view];
/*關(guān)鍵幀動(dòng)畫(huà)
options:
*/
[UIView animateKeyframesWithDuration:5.0 delay:0 options: UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
//第二個(gè)關(guān)鍵幀(準(zhǔn)確的說(shuō)第一個(gè)關(guān)鍵幀是開(kāi)始位置):從0秒開(kāi)始持續(xù)50%的時(shí)間,也就是5.0*0.5=2.5秒
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
_imageView.center=CGPointMake(80.0, 220.0);
}];
//第三個(gè)關(guān)鍵幀,從0.5*5.0秒開(kāi)始,持續(xù)5.0*0.25=1.25秒
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{
_imageView.center=CGPointMake(45.0, 300.0);
}];
//第四個(gè)關(guān)鍵幀:從0.75*5.0秒開(kāi)始,持所需5.0*0.25=1.25秒
[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:0.25 animations:^{
_imageView.center=CGPointMake(55.0, 400.0);
}];
} completion:^(BOOL finished) {
NSLog(@"Animation end.");
}];
}
@end
補(bǔ)充--動(dòng)畫(huà)設(shè)置參數(shù)
對(duì)于關(guān)鍵幀動(dòng)畫(huà)也有一些動(dòng)畫(huà)參數(shù)設(shè)置options,UIViewKeyframeAnimationOptions類(lèi)型,和上面基本動(dòng)畫(huà)參數(shù)設(shè)置有些差別,關(guān)鍵幀動(dòng)畫(huà)設(shè)置參數(shù)分為兩類(lèi),可以組合使用:
- 常規(guī)動(dòng)畫(huà)屬性設(shè)置(可以同時(shí)選擇多個(gè)進(jìn)行設(shè)置)
UIViewAnimationOptionLayoutSubviews:動(dòng)畫(huà)過(guò)程中保證子視圖跟隨運(yùn)動(dòng)。
UIViewAnimationOptionAllowUserInteraction:動(dòng)畫(huà)過(guò)程中允許用戶(hù)交互。
UIViewAnimationOptionBeginFromCurrentState:所有視圖從當(dāng)前狀態(tài)開(kāi)始運(yùn)行。
UIViewAnimationOptionRepeat:重復(fù)運(yùn)行動(dòng)畫(huà)。
UIViewAnimationOptionAutoreverse :動(dòng)畫(huà)運(yùn)行到結(jié)束點(diǎn)后仍然以動(dòng)畫(huà)方式回到初始點(diǎn)。
UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套動(dòng)畫(huà)時(shí)間設(shè)置。
UIViewAnimationOptionOverrideInheritedOptions :不繼承父動(dòng)畫(huà)設(shè)置或動(dòng)畫(huà)類(lèi)型。
- 動(dòng)畫(huà)模式設(shè)置(同前面關(guān)鍵幀動(dòng)畫(huà)動(dòng)畫(huà)模式一一對(duì)應(yīng),可以從其中選擇一個(gè)進(jìn)行設(shè)置)
UIViewKeyframeAnimationOptionCalculationModeLinear:連續(xù)運(yùn)算模式。
UIViewKeyframeAnimationOptionCalculationModeDiscrete :離散運(yùn)算模式。
UIViewKeyframeAnimationOptionCalculationModePaced:均勻執(zhí)行運(yùn)算模式。
UIViewKeyframeAnimationOptionCalculationModeCubic:平滑運(yùn)算模式。
UIViewKeyframeAnimationOptionCalculationModeCubicPaced:平滑均勻運(yùn)算模式。
注意:前面說(shuō)過(guò)關(guān)鍵幀動(dòng)畫(huà)有兩種形式,上面演示的是屬性值關(guān)鍵幀動(dòng)畫(huà),路徑關(guān)鍵幀動(dòng)畫(huà)目前UIView還不支持。
轉(zhuǎn)場(chǎng)動(dòng)畫(huà)
從iOS4.0開(kāi)始,UIView直接封裝了轉(zhuǎn)場(chǎng)動(dòng)畫(huà),使用起來(lái)同樣很簡(jiǎn)單。
//
#import "KCMainViewController.h"
#define IMAGE_COUNT 5
@interface KCMainViewController (){
UIImageView *_imageView;
int _currentIndex;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
//定義圖片控件
_imageView=[[UIImageView alloc]init];
_imageView.frame=[UIScreen mainScreen].bounds;
_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)畫(huà)
-(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+1)%IMAGE_COUNT;
}else{
_currentIndex=(_currentIndex-1+IMAGE_COUNT)%IMAGE_COUNT;
}
NSString *imageName=[NSString stringWithFormat:@"%i.jpg",_currentIndex];
return [UIImage imageNamed:imageName];
}
@end
上面的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)演示中,其實(shí)僅僅有一個(gè)視圖UIImageView做轉(zhuǎn)場(chǎng)動(dòng)畫(huà),每次轉(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)畫(huà)設(shè)置參數(shù)完全同基本動(dòng)畫(huà)參數(shù)設(shè)置;同直接使用轉(zhuǎn)場(chǎng)動(dòng)畫(huà)不同的是使用UIView的裝飾方法進(jìn)行轉(zhuǎn)場(chǎng)動(dòng)畫(huà)其動(dòng)畫(huà)效果較少,因?yàn)檫@里無(wú)法直接使用私有API。
上一篇:iOS開(kāi)發(fā)系列:讓你的應(yīng)用“動(dòng)”起來(lái)--CALayer
下一篇:iOS開(kāi)發(fā)之多線程GCD