在iOS中隨處都可以看到絢麗的動畫效果,實現(xiàn)這些動畫的過程并不復雜骏融,今天將帶大家一窺iOS動畫全貌。在這里你可以看到iOS中如何使用圖層精簡非交互式繪圖,如何通過核心動畫創(chuàng)建基礎動畫坯台、關鍵幀動畫、動畫組瘫寝、轉場動畫蜒蕾,如何通過UIView的裝飾方法對這些動畫操作進行簡化等。在今天的文章里您可以看到動畫操作在iOS中是如何簡單和高效焕阿,很多原來想做但是苦于沒有思路的動畫在iOS中將變得越發(fā)簡單:
CALayer簡介
CALayer常用屬性
CALayer繪圖
基礎動畫
關鍵幀動畫
動畫組
轉場動畫
逐幀動畫
基礎動畫
關鍵幀動畫
轉場動畫
目錄
在介紹動畫操作之前我們必須先來了解一個動畫中常用的對象CALayer咪啡。CALayer包含在QuartzCore框架中耻瑟,這是一個跨平臺的框架茂契,既可以用在iOS中又可以用在Mac OS X中。在使用Core Animation開發(fā)動畫的本質就是將CALayer中的內容轉化為位圖從而供硬件操作缸匪,所以要熟練掌握動畫操作必須先來熟悉CALayer褒纲。
在上一篇文章中使用Quartz 2D繪圖時大家其實已經(jīng)用到了CALayer准夷,當利用drawRect:方法繪圖的本質就是繪制到了UIView的layer(屬性)中,可是這個過程大家在上一節(jié)中根本體會不到外厂。但是在Core Animation中我們操作更多的則不再是UIView而是直接面對CALayer。下圖描繪了CALayer和UIView的關系代承,在UIView中有一個layer屬性作為根圖層汁蝶,根圖層上可以放其他子圖層,在UIView中所有能夠看到的內容都包含在layer中:
在iOS中CALayer的設計主要是了為了內容展示和動畫操作,CALayer本身并不包含在UIKit中掖棉,它不能響應事件墓律。由于CALayer在設計之初就考慮它的動畫操作功能,CALayer很多屬性在修改時都能形成動畫效果幔亥,這種屬性稱為“隱式動畫屬性”耻讽。但是對于UIView的根圖層而言屬性的修改并不形成動畫效果,因為很多情況下根圖層更多的充當容器的做用帕棉,如果它的屬性變動形成動畫效果會直接影響子圖層针肥。另外,UIView的根圖層創(chuàng)建工作完全由iOS負責完成香伴,無法重新創(chuàng)建慰枕,但是可以往根圖層中添加子圖層或移除子圖層。
下表列出了CALayer常用的屬性:
屬性說明是否支持隱式動畫
anchorPoint和中心點position重合的一個點即纲,稱為“錨點”具帮,錨點的描述是相對于x、y位置比例而言的默認在圖像中心點(0.5,0.5)的位置是
backgroundColor圖層背景顏色是
borderColor邊框顏色是
borderWidth邊框寬度是
bounds圖層大小是
contents圖層顯示內容低斋,例如可以將圖片作為圖層內容顯示是
contentsRect圖層顯示內容的大小和位置是
cornerRadius圓角半徑是
doubleSided圖層背面是否顯示蜂厅,默認為YES否
frame圖層大小和位置,不支持隱式動畫膊畴,所以CALayer中很少使用frame掘猿,通常使用bounds和position代替否
hidden是否隱藏是
mask圖層蒙版是
maskToBounds子圖層是否剪切圖層邊界,默認為NO是
opacity透明度 巴比,類似于UIView的alpha是
position圖層中心點位置术奖,類似于UIView的center是
shadowColor陰影顏色是
shadowOffset陰影偏移量是
shadowOpacity陰影透明度,注意默認為0轻绞,如果設置陰影必須設置此屬性是
shadowPath陰影的形狀是
shadowRadius陰影模糊半徑是
sublayers子圖層是
sublayerTransform子圖層形變是
transform圖層形變是
隱式屬性動畫的本質是這些屬性的變動默認隱含了CABasicAnimation動畫實現(xiàn)采记,詳情大家可以參照Xcode幫助文檔中“Animatable Properties”一節(jié)。
在CALayer中很少使用frame屬性政勃,因為frame本身不支持動畫效果唧龄,通常使用bounds和position代替。
CALayer中透明度使用opacity表示而不是alpha奸远;中心點使用position表示而不是center既棺。
anchorPoint屬性是圖層的錨點,范圍在(0~1,0~1)表示在x懒叛、y軸的比例丸冕,這個點永遠可以同position(中心點)重合,當圖層中心點固定后薛窥,調整anchorPoint即可達到調整圖層顯示位置的作用(因為它永遠和position重合)
為了進一步說明anchorPoint的作用胖烛,假設有一個層大小100*100眼姐,現(xiàn)在中心點位置(50,50),由此可以得出frame(0,0,100,100)佩番。上面說過anchorPoint默認為(0.5,0.5)众旗,同中心點position重合,此時使用圖形描述如圖1趟畏;當修改anchorPoint為(0,0)贡歧,此時錨點處于圖層左上角,但是中心點poition并不會改變赋秀,因此圖層會向右下角移動利朵,如圖2;然后修改anchorPoint為(1,1,)沃琅,position還是保持位置不變哗咆,錨點處于圖層右下角,此時圖層如圖3益眉。
下面通過一個簡單的例子演示一下上面幾個屬性晌柬,程序初始化階段我們定義一個正方形,但是圓角路徑調整為正方形邊長的一般郭脂,使其看起來是一個圓形年碘,在點擊屏幕的時候修改圖層的屬性形成動畫效果(注意在程序中沒有直接修改UIView的layer屬性,因為根圖層無法形成動畫效果):
//
//? KCMainViewController.m
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#defineWIDTH 50
@interfaceKCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];// Do any additional setup after loading the view.[self drawMyLayer];
}#pragmamark 繪制圖層
-(void)drawMyLayer{
CGSize size=[UIScreen mainScreen].bounds.size;//獲得根圖層CALayer *layer=[[CALayer alloc]init];//設置背景顏色,由于QuartzCore是跨平臺框架展鸡,無法直接使用UIColorlayer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;//設置中心點layer.position=CGPointMake(size.width/2, size.height/2);//設置大小layer.bounds=CGRectMake(0, 0, WIDTH,WIDTH);//設置圓角,當圓角半徑等于矩形的一半時看起來就是一個圓形layer.cornerRadius=WIDTH/2;//設置陰影l(fā)ayer.shadowColor=[UIColor grayColor].CGColor;
layer.shadowOffset=CGSizeMake(2, 2);
layer.shadowOpacity=.9;//設置邊框
//? ? layer.borderColor=[UIColor whiteColor].CGColor;
//? ? layer.borderWidth=1;
//設置錨點
//? ? layer.anchorPoint=CGPointZero;[self.view.layer addSublayer:layer];
}#pragmamark 點擊放大
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=[touches anyObject];
CALayer *layer=self.view.layer.sublayers[0];
CGFloat width=layer.bounds.size.width;if(width==WIDTH) {
width=WIDTH*4;
}else{
width=WIDTH;
}
layer.bounds=CGRectMake(0, 0, width, width);
layer.position=[touch locationInView:self.view];
layer.cornerRadius=width/2;
}
@end
運行效果:
上一篇文章中重點討論了使用Quartz 2D繪圖屿衅,當時調用了UIView的drawRect:方法繪制圖形、圖像莹弊,這種方式本質還是在圖層中繪制涤久,但是這里會著重介紹一下如何直接在圖層中繪圖。在圖層中繪圖的方式跟原來基本沒有區(qū)別忍弛,只是drawRect:方法是由UIKit組件進行調用响迂,因此里面可以使用一些UIKit封裝的方法進行繪圖,而直接繪制到圖層的方法由于并非UIKit直接調用因此只能用原生的Core Graphics方法繪制细疚。
圖層繪圖有兩種方法蔗彤,不管使用哪種方法繪制完必須調用圖層的setNeedDisplay方法(注意是圖層的方法,不是UIView的方法疯兼,前面我們介紹過UIView也有此方法)
通過圖層代理drawLayer: inContext:方法繪制
通過自定義圖層drawInContext:方法繪制
通過代理方法進行圖層繪圖只要指定圖層的代理然遏,然后在代理對象中重寫-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx方法即可。需要注意這個方法雖然是代理方法但是不用手動實現(xiàn)CALayerDelegate吧彪,因為CALayer定義中給NSObject做了分類擴展待侵,所有的NSObject都包含這個方法。另外設置完代理后必須要調用圖層的setNeedDisplay方法姨裸,否則繪制的內容無法顯示秧倾。
下面的代碼演示了在一個自定義圖層繪制一張圖像并將圖像設置成圓形香拉,這種效果在很多應用中很常見,如最新版的手機QQ頭像就是這種效果:
//
//? KCMainViewController.m
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#definePHOTO_HEIGHT 150
@interfaceKCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//自定義圖層CALayer *layer=[[CALayer alloc]init];
layer.bounds=CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT);
layer.position=CGPointMake(160, 200);
layer.backgroundColor=[UIColor redColor].CGColor;
layer.cornerRadius=PHOTO_HEIGHT/2;//注意僅僅設置圓角中狂,對于圖形而言可以正常顯示,但是對于圖層中繪制的圖片無法正確顯示
//如果想要正確顯示則必須設置masksToBounds=YES扑毡,剪切子圖層layer.masksToBounds=YES;//陰影效果無法和masksToBounds同時使用胃榕,因為masksToBounds的目的就是剪切外邊框,
//而陰影效果剛好在外邊框
//? ? layer.shadowColor=[UIColor grayColor].CGColor;
//? ? layer.shadowOffset=CGSizeMake(2, 2);
//? ? layer.shadowOpacity=1;
//設置邊框layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=2;//設置圖層代理layer.delegate=self;//添加圖層到根圖層[self.view.layer addSublayer:layer];//調用圖層setNeedDisplay,否則代理方法不會被調用[layer setNeedsDisplay];
}#pragmamark 繪制圖形瞄摊、圖像到圖層勋又,注意參數(shù)中的ctx是圖層的圖形上下文,其中繪圖位置也是相對圖層而言的
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{//? ? NSLog(@"%@",layer);//這個圖層正是上面定義的圖層CGContextSaveGState(ctx);//圖形上下文形變换帜,解決圖片倒立的問題CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -PHOTO_HEIGHT);
UIImage *image=[UIImage imageNamed:@"photo.png"];//注意這個位置是相對于圖層而言的不是屏幕CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);//? ? CGContextFillRect(ctx, CGRectMake(0, 0, 100, 100));
//? ? CGContextDrawPath(ctx, kCGPathFillStroke);CGContextRestoreGState(ctx);
}
@end
運行效果:
使用代理方法繪制圖形楔壤、圖像時在drawLayer:inContext:方法中可以通過事件參數(shù)獲得繪制的圖層和圖形上下文。在這個方法中繪圖時所有的位置都是相對于圖層而言的惯驼,圖形上下文指的也是當前圖層的圖形上下文蹲嚣。
需要注意的是上面代碼中繪制圖片圓形裁切效果時如果不設置masksToBounds是無法顯示圓形,但是對于其他圖形卻沒有這個限制祟牲。原因就是當繪制一張圖片到圖層上的時候會重新創(chuàng)建一個圖層添加到當前圖層隙畜,這樣一來如果設置了圓角之后雖然底圖層有圓角效果,但是子圖層還是矩形说贝,只有設置了masksToBounds為YES讓子圖層按底圖層剪切才能顯示圓角效果议惰。同樣的,有些朋友經(jīng)常在網(wǎng)上提問說為什么使用UIImageView的layer設置圓角后圖片無法顯示圓角乡恕,只有設置masksToBounds才能出現(xiàn)效果言询,也是類似的問題。
如果設置了masksToBounds=YES之后確實可以顯示圖片圓角效果傲宜,但遺憾的是設置了這個屬性之后就無法設置陰影效果运杭。因為masksToBounds=YES就意味著外邊框不能顯示,而陰影恰恰作為外邊框繪制的蛋哭,這樣兩個設置就產(chǎn)生了矛盾县习。要解決這個問題不妨換個思路:使用兩個大小一樣的圖層,下面的圖層負責繪制陰影谆趾,上面的圖層用來顯示圖片躁愿。
//
//? KCMainViewController.m
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#definePHOTO_HEIGHT 150
@interfaceKCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGPoint position= CGPointMake(160, 200);
CGRect bounds=CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT);
CGFloat cornerRadius=PHOTO_HEIGHT/2;
CGFloat borderWidth=2;//陰影圖層CALayer *layerShadow=[[CALayer alloc]init];
layerShadow.bounds=bounds;
layerShadow.position=position;
layerShadow.cornerRadius=cornerRadius;
layerShadow.shadowColor=[UIColor grayColor].CGColor;
layerShadow.shadowOffset=CGSizeMake(2, 1);
layerShadow.shadowOpacity=1;
layerShadow.borderColor=[UIColor whiteColor].CGColor;
layerShadow.borderWidth=borderWidth;
[self.view.layer addSublayer:layerShadow];//容器圖層CALayer *layer=[[CALayer alloc]init];
layer.bounds=bounds;
layer.position=position;
layer.backgroundColor=[UIColor redColor].CGColor;
layer.cornerRadius=cornerRadius;
layer.masksToBounds=YES;
layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=borderWidth;//設置圖層代理layer.delegate=self;//添加圖層到根圖層[self.view.layer addSublayer:layer];//調用圖層setNeedDisplay,否則代理方法不會被調用[layer setNeedsDisplay];
}#pragmamark 繪制圖形、圖像到圖層沪蓬,注意參數(shù)中的ctx是圖層的圖形上下文彤钟,其中繪圖位置也是相對圖層而言的
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{//? ? NSLog(@"%@",layer);//這個圖層正是上面定義的圖層CGContextSaveGState(ctx);//圖形上下文形變,解決圖片倒立的問題CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -PHOTO_HEIGHT);
UIImage *image=[UIImage imageNamed:@"photo.jpg"];//注意這個位置是相對于圖層而言的不是屏幕CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);//? ? CGContextFillRect(ctx, CGRectMake(0, 0, 100, 100));
//? ? CGContextDrawPath(ctx, kCGPathFillStroke);CGContextRestoreGState(ctx);
}
@end
運行效果:
從上面代碼中大家不難發(fā)現(xiàn)使用Core Graphics繪制圖片時會倒立顯示跷叉,對圖層的圖形上下文進行了反轉逸雹。在前一篇文章中也采用了類似的方法去解決這個問題营搅,但是在那篇文章中也提到過如果直接讓圖像沿著x軸旋轉180度同樣可以達到正確顯示的目的,只是當時的旋轉靠圖形上下文還無法繞x軸旋轉梆砸。今天學習了圖層之后转质,其實可以控制圖層直接旋轉而不用借助于圖形上下文的形變操作,而且這么操作起來會更加簡單和直觀帖世。對于上面的程序休蟹,只需要設置圖層的transform屬性即可。需要注意的是transform是CATransform3D類型日矫,形變可以在三個維度上進行赂弓,使用方法和前面介紹的二維形變是類似的,而且都有對應的形變設置方法(如:CATransform3DMakeTranslation()哪轿、CATransform3DMakeScale()盈魁、CATransform3DMakeRotation())。下面的代碼通過CATransform3DMakeRotation()方法在x軸旋轉180度解決倒立問題:
//
//? 形變演示
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#definePHOTO_HEIGHT 150
@interfaceKCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGPoint position= CGPointMake(160, 200);
CGRect bounds=CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT);
CGFloat cornerRadius=PHOTO_HEIGHT/2;
CGFloat borderWidth=2;//陰影圖層CALayer *layerShadow=[[CALayer alloc]init];
layerShadow.bounds=bounds;
layerShadow.position=position;
layerShadow.cornerRadius=cornerRadius;
layerShadow.shadowColor=[UIColor grayColor].CGColor;
layerShadow.shadowOffset=CGSizeMake(2, 1);
layerShadow.shadowOpacity=1;
layerShadow.borderColor=[UIColor whiteColor].CGColor;
layerShadow.borderWidth=borderWidth;
[self.view.layer addSublayer:layerShadow];//容器圖層CALayer *layer=[[CALayer alloc]init];
layer.bounds=bounds;
layer.position=position;
layer.backgroundColor=[UIColor redColor].CGColor;
layer.cornerRadius=cornerRadius;
layer.masksToBounds=YES;
layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=borderWidth;//利用圖層形變解決圖像倒立問題layer.transform=CATransform3DMakeRotation(M_PI, 1, 0, 0);//設置圖層代理layer.delegate=self;//添加圖層到根圖層[self.view.layer addSublayer:layer];//調用圖層setNeedDisplay,否則代理方法不會被調用[layer setNeedsDisplay];
}#pragmamark 繪制圖形窃诉、圖像到圖層杨耙,注意參數(shù)中的ctx時圖層的圖形上下文,其中繪圖位置也是相對圖層而言的
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{//? ? NSLog(@"%@",layer);//這個圖層正是上面定義的圖層UIImage *image=[UIImage imageNamed:@"photo.jpg"];//注意這個位置是相對于圖層而言的不是屏幕CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);
}
@end
事實上如果僅僅就顯示一張圖片在圖層中當然沒有必要那么麻煩飘痛,直接設置圖層contents就可以了按脚,不牽涉到繪圖也就沒有倒立的問題了。
//
//? 圖層內容設置
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#definePHOTO_HEIGHT 150
@interfaceKCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
CGPoint position= CGPointMake(160, 200);
CGRect bounds=CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT);
CGFloat cornerRadius=PHOTO_HEIGHT/2;
CGFloat borderWidth=2;//陰影圖層CALayer *layerShadow=[[CALayer alloc]init];
layerShadow.bounds=bounds;
layerShadow.position=position;
layerShadow.cornerRadius=cornerRadius;
layerShadow.shadowColor=[UIColor grayColor].CGColor;
layerShadow.shadowOffset=CGSizeMake(2, 1);
layerShadow.shadowOpacity=1;
layerShadow.borderColor=[UIColor whiteColor].CGColor;
layerShadow.borderWidth=borderWidth;
[self.view.layer addSublayer:layerShadow];//容器圖層CALayer *layer=[[CALayer alloc]init];
layer.bounds=bounds;
layer.position=position;
layer.backgroundColor=[UIColor redColor].CGColor;
layer.cornerRadius=cornerRadius;
layer.masksToBounds=YES;
layer.borderColor=[UIColor whiteColor].CGColor;
layer.borderWidth=borderWidth;//設置內容(注意這里一定要轉換為CGImage)UIImage *image=[UIImage imageNamed:@"photo.jpg"];//? ? layer.contents=(id)image.CGImage;[layer setContents:(id)image.CGImage];//添加圖層到根圖層[self.view.layer addSublayer:layer];
}
@end
[layer setValue:@M_PI forKeyPath:@"transform.rotation.x"];
當然践险,通過key path設置形變參數(shù)就需要了解有哪些key path可以設置猿妈,這里就不再一一列舉,大家可以參照Xcode幫助文檔中“CATransform3D Key Paths”一節(jié)巍虫,里面描述的很詳細彭则。
在自定義圖層中繪圖時只要自己編寫一個類繼承于CALayer然后在drawInContext:中繪圖即可。同前面在代理方法繪圖一樣占遥,要顯示圖層中繪制的內容也要調用圖層的setNeedDisplay方法俯抖,否則drawInContext方法將不會調用。
前面的文章中曾經(jīng)說過瓦胎,在使用Quartz 2D在UIView中繪制圖形的本質也是繪制到圖層中芬萍,為了說明這個問題下面演示自定義圖層繪圖時沒有直接在視圖控制器中調用自定義圖層尤揣,而是在一個UIView將自定義圖層添加到UIView的根圖層中(例子中的UIView跟自定義圖層繪圖沒有直接關系)。從下面的代碼中可以看到:UIView在顯示時其根圖層會自動創(chuàng)建一個CGContextRef(CALayer本質使用的是位圖上下文)柬祠,同時調用圖層代理(UIView創(chuàng)建圖層會自動設置圖層代理為其自身)的draw: inContext:方法并將圖形上下文作為參數(shù)傳遞給這個方法北戏。而在UIView的draw:inContext:方法中會調用其drawRect:方法,在drawRect:方法中使用UIGraphicsGetCurrentContext()方法得到的上下文正是前面創(chuàng)建的上下文漫蛔。
KCLayer.m
//
//? KCLayer.m
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCLayer.h"@implementation KCLayer
-(void)drawInContext:(CGContextRef)ctx{
NSLog(@"3-drawInContext:");
NSLog(@"CGContext:%@",ctx);//? ? CGContextRotateCTM(ctx, M_PI_4);CGContextSetRGBFillColor(ctx, 135.0/255.0, 232.0/255.0, 84.0/255.0, 1);
CGContextSetRGBStrokeColor(ctx, 135.0/255.0, 232.0/255.0, 84.0/255.0, 1);//? ? CGContextFillRect(ctx, CGRectMake(0, 0, 100, 100));
//? ? CGContextFillEllipseInRect(ctx, CGRectMake(50, 50, 100, 100));CGContextMoveToPoint(ctx, 94.5, 33.5);//// Star DrawingCGContextAddLineToPoint(ctx,104.02, 47.39);
CGContextAddLineToPoint(ctx,120.18, 52.16);
CGContextAddLineToPoint(ctx,109.91, 65.51);
CGContextAddLineToPoint(ctx,110.37, 82.34);
CGContextAddLineToPoint(ctx,94.5, 76.7);
CGContextAddLineToPoint(ctx,78.63, 82.34);
CGContextAddLineToPoint(ctx,79.09, 65.51);
CGContextAddLineToPoint(ctx,68.82, 52.16);
CGContextAddLineToPoint(ctx,84.98, 47.39);
CGContextClosePath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
}
@end
KCView.m
//
//? KCView.m
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCView.h"#import"KCLayer.h"@implementation KCView
-(instancetype)initWithFrame:(CGRect)frame{
NSLog(@"initWithFrame:");if(self=[super initWithFrame:frame]) {
KCLayer *layer=[[KCLayer alloc]init];
layer.bounds=CGRectMake(0, 0, 185, 185);
layer.position=CGPointMake(160,284);
layer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;//顯示圖層[layer setNeedsDisplay];
[self.layer addSublayer:layer];
}returnself;
}
-(void)drawRect:(CGRect)rect{
NSLog(@"2-drawRect:");
NSLog(@"CGContext:%@",UIGraphicsGetCurrentContext());//得到的當前圖形上下文正是drawLayer中傳遞的[super drawRect:rect];
}
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
NSLog(@"1-drawLayer:inContext:");
NSLog(@"CGContext:%@",ctx);
[super drawLayer:layer inContext:ctx];
}
@end
KCMainViewController.m
//
//? KCMainViewController.m
//? CALayer
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#import"KCView.h"@interfaceKCMainViewController ()
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
KCView *view=[[KCView alloc]initWithFrame:[UIScreen mainScreen].bounds];
view.backgroundColor=[UIColor colorWithRed:249.0/255.0 green:249.0/255.0 blue:249.0/255.0 alpha:1];
[self.view addSubview:view];
}
@end
運行效果:
大家都知道在iOS中實現(xiàn)一個動畫相當簡單最欠,只要調用UIView的塊代碼即可實現(xiàn)一個動畫效果,這在其他系統(tǒng)開發(fā)中基本不可能實現(xiàn)惩猫。下面通過一個簡單的UIView進行一個圖片放大動畫效果演示:
//
//? KCMainViewController.m
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController ()
@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];//兩秒后開始一個持續(xù)一分鐘的動畫[UIView animateWithDuration:1 delay:2 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
imageView.frame=CGRectMake(80, 100, 160, 160);
} completion:nil];
}
@end
使用上面UIView封裝的方法進行動畫設置固然十分方便,但是具體動畫如何實現(xiàn)我們是不清楚的蚜点,而且上面的代碼還有一些問題是無法解決的轧房,例如:如何控制動畫的暫停?如何進行動畫的組合绍绘?奶镶。。陪拘。
這里就需要了解iOS的核心動畫Core Animation(包含在Quartz Core框架中)厂镇。在iOS中核心動畫分為幾類:基礎動畫、關鍵幀動畫左刽、動畫組捺信、轉場動畫。各個類的關系大致如下:
CAAnimation:核心動畫的基礎類欠痴,不能直接使用迄靠,負責動畫運行時間、速度的控制喇辽,本身實現(xiàn)了CAMediaTiming協(xié)議掌挚。
CAPropertyAnimation:屬性動畫的基類(通過屬性進行動畫設置,注意是可動畫屬性)菩咨,不能直接使用吠式。
CAAnimationGroup:動畫組,動畫組是一種組合模式設計抽米,可以通過動畫組來進行所有動畫行為的統(tǒng)一控制特占,組中所有動畫效果可以并發(fā)執(zhí)行。
CATransition:轉場動畫云茸,主要通過濾鏡進行動畫效果設置摩钙。
CABasicAnimation:基礎動畫,通過屬性修改進行動畫參數(shù)控制查辩,只有初始狀態(tài)和結束狀態(tài)胖笛。
CAKeyframeAnimation:關鍵幀動畫网持,同樣是通過屬性進行動畫參數(shù)控制,但是同基礎動畫不同的是它可以有多個狀態(tài)控制长踊。
基礎動畫功舀、關鍵幀動畫都屬于屬性動畫,就是通過修改屬性值產(chǎn)生動畫效果身弊,開發(fā)人員只需要設置初始值和結束值辟汰,中間的過程動畫(又叫“補間動畫”)由系統(tǒng)自動計算產(chǎn)生。和基礎動畫不同的是關鍵幀動畫可以設置多個屬性值阱佛,每兩個屬性中間的補間動畫由系統(tǒng)自動完成帖汞,因此從這個角度而言基礎動畫又可以看成是有兩個關鍵幀的關鍵幀動畫。
在開發(fā)過程中很多情況下通過基礎動畫就可以滿足開發(fā)需求凑术,前面例子中使用的UIView代碼塊進行圖像放大縮小的演示動畫也是基礎動畫(在iOS7中UIView也對關鍵幀動畫進行了封裝)翩蘸,只是UIView裝飾方法隱藏了更多的細節(jié)。如果不使用UIView封裝的方法淮逊,動畫創(chuàng)建一般分為以下幾步:
1.初始化動畫并設置動畫屬性
2.設置動畫屬性初始值(可以省略)催首、結束值以及其他動畫屬性
3.給圖層添加動畫
下面以一個移動動畫為例進行演示,在這個例子中點擊屏幕哪個位置落花將飛向哪里泄鹏。
//
//? KCMainViewController.m
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景(注意這個圖片其實在根圖層)UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];//自定義一個圖層_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];
}#pragmamark 移動動畫
-(void)translatonAnimation:(CGPoint)location{//1.創(chuàng)建動畫并指定動畫屬性CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];//2.設置動畫屬性初始值和結束值
//? ? basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設置郎任,默認為圖層初始狀態(tài)basicAnimation.toValue=[NSValue valueWithCGPoint:location];//設置其他動畫屬性basicAnimation.duration=5.0;//動畫時間5秒
//basicAnimation.repeatCount=HUGE_VALF;//設置重復次數(shù),HUGE_VALF可看做無窮大,起到循環(huán)動畫的效果
//? ? basicAnimation.removedOnCompletion=NO;//運行一次是否移除動畫
//3.添加動畫到圖層备籽,注意key相當于給動畫進行命名舶治,以后獲得該動畫時可以使用此名稱獲取[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}#pragmamark 點擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];//創(chuàng)建并開始動畫[self translatonAnimation:location];
}
@end
運行效果:
上面實現(xiàn)了一個基本動畫效果,但是這個動畫存在一個問題:動畫結束后動畫圖層回到了原來的位置车猬,當然是用UIView封裝的方法是沒有這個問題的歼疮。如何解決這個問題呢?
前面說過圖層動畫的本質就是將圖層內部的內容轉化為位圖經(jīng)硬件操作形成一種動畫效果诈唬,其實圖層本身并沒有任何的變化韩脏。上面的動畫中圖層并沒有因為動畫效果而改變它的位置(對于縮放動畫其大小也是不會改變的),所以動畫完成之后圖層還是在原來的顯示位置沒有任何變化铸磅,如果這個圖層在一個UIView中你會發(fā)現(xiàn)在UIView移動過程中你要觸發(fā)UIView的點擊事件也只能點擊原來的位置(即使它已經(jīng)運動到了別的位置)唆姐,因為它的位置從來沒有變過颅悉。當然解決這個問題方法比較多,這里不妨在動畫完成之后重新設置它的位置。
//
//? KCMainViewController.m
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景(注意這個圖片其實在根圖層)UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];//自定義一個圖層_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];
}#pragmamark 移動動畫
-(void)translatonAnimation:(CGPoint)location{//1.創(chuàng)建動畫并指定動畫屬性CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];//2.設置動畫屬性初始值和結束值
//? ? basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設置盛霎,默認為圖層初始狀態(tài)basicAnimation.toValue=[NSValue valueWithCGPoint:location];//設置其他動畫屬性basicAnimation.duration=5.0;//動畫時間5秒
//basicAnimation.repeatCount=HUGE_VALF;//設置重復次數(shù),HUGE_VALF可看做無窮大铆农,起到循環(huán)動畫的效果
//? ? basicAnimation.removedOnCompletion=NO;//運行一次是否移除動畫basicAnimation.delegate=self;//存儲當前位置在動畫結束后使用[basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];//3.添加動畫到圖層嫁怀,注意key相當于給動畫進行命名锅铅,以后獲得該動畫時可以使用此名稱獲取[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}#pragmamark 點擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];//創(chuàng)建并開始動畫[self translatonAnimation:location];
}#pragmamark - 動畫代理方法#pragmamark 動畫開始
-(void)animationDidStart:(CAAnimation *)anim{
NSLog(@"animation(%@) start.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
NSLog(@"%@",[_layer animationForKey:@"KCBasicAnimation_Translation"]);//通過前面的設置的key獲得動畫}#pragmamark 動畫結束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
}
@end
上面通過給動畫設置一個代理去監(jiān)聽動畫的開始和結束事件,在動畫開始前給動畫添加一個自定義屬性“KCBasicAnimationLocation”存儲動畫終點位置,然后在動畫結束后設置動畫的位置為終點位置界轩。
如果運行上面的代碼大家可能會發(fā)現(xiàn)另外一個問題画饥,那就是動畫運行完成后會重新從起始點運動到終點。這個問題產(chǎn)生的原因就是前面提到的浊猾,對于非根圖層抖甘,設置圖層的可動畫屬性(在動畫結束后重新設置了position,而position是可動畫屬性)會產(chǎn)生動畫效果葫慎。解決這個問題有兩種辦法:關閉圖層隱式動畫衔彻、設置動畫圖層為根圖層。顯然這里不能采取后者偷办,因為根圖層當前已經(jīng)作為動畫的背景艰额。
要關閉隱式動畫需要用到動畫事務CATransaction,在事務內將隱式動畫關閉椒涯,例如上面的代碼可以改為:
#pragmamark 動畫結束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));//開啟事務[CATransaction begin];//禁用隱式動畫[CATransaction setDisableActions:YES];
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];//提交事務[CATransaction commit];
}
補充
上面通過在animationDidStop中重新設置動畫的位置主要為了說明隱式動畫關閉和動畫事件之間傳參的內容柄沮,有朋友發(fā)現(xiàn)這種方式有可能在動畫運行完之后出現(xiàn)從原點瞬間回到終點的過程,最早在調試的時候沒有發(fā)現(xiàn)這個問題逐工,這里感謝這位朋友。其實解決這個問題并不難漂辐,首先必須設置fromValue泪喊,其次在動畫開始前設置動畫position為終點位置(當然也必須關閉隱式動畫)。但是這里主要還是出于學習的目的髓涯,真正開發(fā)的時候做平移動畫直接使用隱式動畫即可袒啼,沒有必要那么麻煩。
當然上面的動畫還顯得有些生硬纬纪,因為落花飄散的時候可能不僅僅是自由落體運動蚓再,本身由于空氣阻力、外界風力還會造成落花在空中的旋轉包各、搖擺等摘仅,這里不妨給圖層添加一個旋轉的動畫。對于圖層的旋轉前面已經(jīng)演示過怎么通過key path設置圖層旋轉的內容了问畅,在這里需要強調一下娃属,圖層的形變都是基于錨點進行的。例如旋轉护姆,旋轉的中心點就是圖層的錨點矾端。
//
//? KCMainViewController.m
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景(注意這個圖片其實在根圖層)UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];//自定義一個圖層_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.anchorPoint=CGPointMake(0.5, 0.6);//設置錨點_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
}#pragmamark 移動動畫
-(void)translatonAnimation:(CGPoint)location{//1.創(chuàng)建動畫并指定動畫屬性CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];//2.設置動畫屬性初始值、結束值
//? ? basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設置卵皂,默認為圖層初始狀態(tài)basicAnimation.toValue=[NSValue valueWithCGPoint:location];//設置其他動畫屬性basicAnimation.duration=5.0;//動畫時間5秒
//basicAnimation.repeatCount=HUGE_VALF;//設置重復次數(shù),HUGE_VALF可看做無窮大秩铆,起到循環(huán)動畫的效果
//? ? basicAnimation.removedOnCompletion=NO;//運行一次是否移除動畫basicAnimation.delegate=self;//存儲當前位置在動畫結束后使用[basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];//3.添加動畫到圖層,注意key相當于給動畫進行命名灯变,以后獲得該圖層時可以使用此名稱獲取[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}#pragmamark 旋轉動畫
-(void)rotationAnimation{//1.創(chuàng)建動畫并指定動畫屬性CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];//2.設置動畫屬性初始值殴玛、結束值
//? ? basicAnimation.fromValue=[NSNumber numberWithInt:M_PI_2];basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];//設置其他動畫屬性basicAnimation.duration=6.0;
basicAnimation.autoreverses=true;//旋轉后再旋轉到原來的位置
//4.添加動畫到圖層捅膘,注意key相當于給動畫進行命名,以后獲得該動畫時可以使用此名稱獲取[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}#pragmamark 點擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];//創(chuàng)建并開始動畫[self translatonAnimation:location];
[self rotationAnimation];
}#pragmamark - 動畫代理方法#pragmamark 動畫開始
-(void)animationDidStart:(CAAnimation *)anim{
NSLog(@"animation(%@) start.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
NSLog(@"%@",[_layer animationForKey:@"KCBasicAnimation_Translation"]);//通過前面的設置的key獲得動畫}#pragmamark 動畫結束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));//開啟事務[CATransaction begin];//禁用隱式動畫[CATransaction setDisableActions:YES];
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];//提交事務[CATransaction commit];
}
@end
上面代碼中結合兩種動畫操作族阅,需要注意的是只給移動動畫設置了代理篓跛,在旋轉動畫中并沒有設置代理,否則代理方法會執(zhí)行兩遍坦刀。由于旋轉動畫會無限循環(huán)執(zhí)行(上面設置了重復次數(shù)無窮大)愧沟,并且兩個動畫的執(zhí)行時間沒有必然的關系,這樣一來移動停止后可能還在旋轉鲤遥,為了讓移動動畫停止后旋轉動畫停止就需要使用到動畫的暫停和恢復方法沐寺。
核心動畫的運行有一個媒體時間的概念,假設將一個旋轉動畫設置旋轉一周用時60秒的話盖奈,那么當動畫旋轉90度后媒體時間就是15秒混坞。如果此時要將動畫暫停只需要讓媒體時間偏移量設置為15秒即可,并把動畫運行速度設置為0使其停止運動钢坦。類似的究孕,如果又過了60秒后需要恢復動畫(此時媒體時間為75秒),這時只要將動畫開始開始時間設置為當前媒體時間75秒減去暫停時的時間(也就是之前定格動畫時的偏移量)15秒(開始時間=75-15=60秒)爹凹,那么動畫就會重新計算60秒后的狀態(tài)再開始運行厨诸,與此同時將偏移量重新設置為0并且把運行速度設置1。這個過程中真正起到暫停動畫和恢復動畫的其實是動畫速度的調整禾酱,媒體時間偏移量以及恢復時的開始時間設置主要為了讓動畫更加連貫微酬。
下面的代碼演示了移動動畫結束后旋轉動畫暫停,并且當再次點擊動畫時旋轉恢復的過程(注意在移動過程中如果再次點擊屏幕可以暫停移動和旋轉動畫颤陶,再次點擊可以恢復兩種動畫颗管。但是當移動結束后觸發(fā)了移動動畫的完成事件如果再次點擊屏幕則只能恢復旋轉動畫,因為此時移動動畫已經(jīng)結束而不是暫停滓走,無法再恢復)垦江。
//
//? KCMainViewController.m
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景(注意這個圖片其實在根圖層)UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];//自定義一個圖層_layer=[[CALayer alloc]init];
_layer.bounds=CGRectMake(0, 0, 10, 20);
_layer.position=CGPointMake(50, 150);
_layer.anchorPoint=CGPointMake(0.5, 0.6);//設置錨點_layer.contents=(id)[UIImage imageNamed:@"petal.png"].CGImage;
[self.view.layer addSublayer:_layer];
}#pragmamark 移動動畫
-(void)translatonAnimation:(CGPoint)location{//1.創(chuàng)建動畫并指定動畫屬性CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];//2.設置動畫屬性初始值、結束值
//? ? basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設置搅方,默認為圖層初始狀態(tài)basicAnimation.toValue=[NSValue valueWithCGPoint:location];//設置其他動畫屬性basicAnimation.duration=5.0;//動畫時間5秒
//? ? basicAnimation.repeatCount=HUGE_VALF;//設置重復次數(shù),HUGE_VALF可看做無窮大疫粥,起到循環(huán)動畫的效果basicAnimation.removedOnCompletion=NO;//運行一次是否移除動畫basicAnimation.delegate=self;//存儲當前位置在動畫結束后使用[basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];//3.添加動畫到圖層,注意key相當于給動畫進行命名腰懂,以后獲得該圖層時可以使用此名稱獲取[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
}#pragmamark 旋轉動畫
-(void)rotationAnimation{//1.創(chuàng)建動畫并指定動畫屬性CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];//2.設置動畫屬性初始值梗逮、結束值
//? ? basicAnimation.fromValue=[NSNumber numberWithInt:M_PI_2];basicAnimation.toValue=[NSNumber numberWithFloat:M_PI_2*3];//設置其他動畫屬性basicAnimation.duration=6.0;
basicAnimation.autoreverses=true;//旋轉后在旋轉到原來的位置basicAnimation.repeatCount=HUGE_VALF;//設置無限循環(huán)basicAnimation.removedOnCompletion=NO;//? ? basicAnimation.delegate=self;
//4.添加動畫到圖層,注意key相當于給動畫進行命名绣溜,以后獲得該動畫時可以使用此名稱獲取[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}#pragmamark 點擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];//判斷是否已經(jīng)常見過動畫慷彤,如果已經(jīng)創(chuàng)建則不再創(chuàng)建動畫CAAnimation *animation= [_layer animationForKey:@"KCBasicAnimation_Translation"];if(animation){if(_layer.speed==0) {
[self animationResume];
}else{
[self animationPause];
}
}else{//創(chuàng)建并開始動畫[self translatonAnimation:location];
[self rotationAnimation];
}
}#pragmamark 動畫暫停
-(void)animationPause{//取得指定圖層動畫的媒體時間,后面參數(shù)用于指定子圖層,這里不需要CFTimeInterval interval=[_layer convertTime:CACurrentMediaTime() fromLayer:nil];//設置時間偏移量底哗,保證暫停時停留在旋轉的位置[_layer setTimeOffset:interval];//速度設置為0岁诉,暫停動畫_layer.speed=0;
}#pragmamark 動畫恢復
-(void)animationResume{//獲得暫停的時間CFTimeInterval beginTime= CACurrentMediaTime()- _layer.timeOffset;//設置偏移量_layer.timeOffset=0;//設置開始時間_layer.beginTime=beginTime;//設置動畫速度,開始運動_layer.speed=1.0;
}#pragmamark - 動畫代理方法#pragmamark 動畫開始
-(void)animationDidStart:(CAAnimation *)anim{
NSLog(@"animation(%@) start.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));
NSLog(@"%@",[_layer animationForKey:@"KCBasicAnimation_Translation"]);//通過前面的設置的key獲得動畫}#pragmamark 動畫結束
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"animation(%@) stop.\r_layer.frame=%@",anim,NSStringFromCGRect(_layer.frame));//開啟事務[CATransaction begin];//禁用隱式動畫[CATransaction setDisableActions:YES];
_layer.position=[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];//提交事務[CATransaction commit];//暫停動畫[self animationPause];
}
@end
運行效果:
注意:
動畫暫停針對的是圖層而不是圖層中的某個動畫跋选。
要做無限循環(huán)的動畫涕癣,動畫的removedOnCompletion屬性必須設置為NO,否則運行一次動畫就會銷毀前标。
熟悉flash開發(fā)的朋友對于關鍵幀動畫應該不陌生坠韩,這種動畫方式在flash開發(fā)中經(jīng)常用到。關鍵幀動畫就是在動畫控制過程中開發(fā)者指定主要的動畫狀態(tài)炼列,至于各個狀態(tài)間動畫如何進行則由系統(tǒng)自動運算補充(每兩個關鍵幀之間系統(tǒng)形成的動畫稱為“補間動畫”)只搁,這種動畫的好處就是開發(fā)者不用逐個控制每個動畫幀,而只要關心幾個關鍵幀的狀態(tài)即可俭尖。
關鍵幀動畫開發(fā)分為兩種形式:一種是通過設置不同的屬性值進行關鍵幀控制氢惋,另一種是通過繪制路徑進行關鍵幀控制。后者優(yōu)先級高于前者稽犁,如果設置了路徑則屬性值就不再起作用焰望。
對于前面的落花動畫效果而言其實落花的過程并不自然,很顯然實際生活中它不可能沿著直線下落已亥,這里我們不妨通過關鍵幀動畫的values屬性控制它在下落過程中的屬性熊赖。假設下落過程如圖:
在這里需要設置四個關鍵幀(如圖中四個關鍵點),具體代碼如下(動畫創(chuàng)建過程同基本動畫基本完全一致):
//
//? 通過values設置關鍵幀動畫
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景(注意這個圖片其實在根圖層)UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];//自定義一個圖層_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)建動畫[self translationAnimation];
}#pragmamark 關鍵幀動畫
-(void)translationAnimation{//1.創(chuàng)建關鍵幀動畫并設置動畫屬性CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];//2.設置關鍵幀,這里有四個關鍵幀NSValue *key1=[NSValue valueWithCGPoint:_layer.position];//對于關鍵幀動畫初始值不能省略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;//設置其他屬性keyframeAnimation.duration=8.0;
keyframeAnimation.beginTime=CACurrentMediaTime()+2;//設置延遲2秒執(zhí)行
//3.添加動畫到圖層陷猫,添加動畫后就會執(zhí)行動畫[_layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
}
@end
運行效果(注意運行結束沒有設置圖層位置為動畫運動結束位置):
上面的方式固然比前面使用基礎動畫效果要好一些秫舌,但其實還是存在問題的妖,那就是落花飛落的路徑是直線的绣檬,當然這個直線是根據(jù)程序中設置的四個關鍵幀自動形成的,那么如何讓它沿著曲線飄落呢嫂粟?這就是第二種類型的關鍵幀動畫娇未,通過描繪路徑進行關鍵幀動畫控制。假設讓落花沿著下面的曲線路徑飄落:
當然星虹,這是一條貝塞爾曲線零抬,學習了前篇文章之后相信對于這類曲線應該并不陌生,下面是具體實現(xiàn)代碼:
//
//? 通過path設置關鍵幀動畫
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景(注意這個圖片其實在根圖層)UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];//自定義一個圖層_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)建動畫[self translationAnimation];
}#pragmamark 關鍵幀動畫
-(void)translationAnimation{//1.創(chuàng)建關鍵幀動畫并設置動畫屬性CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];//2.設置路徑
//繪制貝塞爾曲線CGPathRef path=CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, _layer.position.x, _layer.position.y);//移動到起始點CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, 55, 400);//繪制二次貝塞爾曲線keyframeAnimation.path=path;//設置path屬性CGPathRelease(path);//釋放路徑對象
//設置其他屬性keyframeAnimation.duration=8.0;
keyframeAnimation.beginTime=CACurrentMediaTime()+5;//設置延遲2秒執(zhí)行
//3.添加動畫到圖層宽涌,添加動畫后就會執(zhí)行動畫[_layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
}
@end
運行效果(注意運行結束沒有設置圖層位置為動畫運動結束位置):
看起來動畫不會那么生硬了平夜,但是這里需要注意,對于路徑類型的關鍵幀動畫系統(tǒng)是從描繪路徑的位置開始路徑卸亮,直到路徑結束忽妒。如果上面的路徑不是貝塞爾曲線而是矩形路徑那么它會從矩形的左上角開始運行,順時針一周回到左上角;如果指定的路徑是一個橢圓段直,那么動畫運行的路徑是從橢圓右側開始(0度)順時針一周回到右側吃溅。
在關鍵幀動畫中還有一些動畫屬性初學者往往比較容易混淆,這里專門針對這些屬性做一下介紹鸯檬。
keyTimes:各個關鍵幀的時間控制决侈。前面使用values設置了四個關鍵幀,默認情況下每兩幀之間的間隔為:8/(4-1)秒喧务。如果想要控制動畫從第一幀到第二針占用時間4秒赖歌,從第二幀到第三幀時間為2秒,而從第三幀到第四幀時間2秒的話蹂楣,就可以通過keyTimes進行設置俏站。keyTimes中存儲的是時間占用比例點,此時可以設置keyTimes的值為0.0痊土,0.5肄扎,0.75,1.0(當然必須轉換為NSNumber)赁酝,也就是說1到2幀運行到總時間的50%犯祠,2到3幀運行到總時間的75%,3到4幀運行到8秒結束酌呆。
caculationMode:動畫計算模式衡载。還拿上面keyValues動畫舉例,之所以1到2幀能形成連貫性動畫而不是直接從第1幀經(jīng)過8/3秒到第2幀是因為動畫模式是連續(xù)的(值為kCAAnimationLinear隙袁,這是計算模式的默認值)痰娱;而如果指定了動畫模式為kCAAnimationDiscrete離散的那么你會看到動畫從第1幀經(jīng)過8/3秒直接到第2幀,中間沒有任何過渡菩收。其他動畫模式還有:kCAAnimationPaced(均勻執(zhí)行梨睁,會忽略keyTimes)、kCAAnimationCubic(平滑執(zhí)行娜饵,對于位置變動關鍵幀動畫運行軌跡更平滑)坡贺、kCAAnimationCubicPaced(平滑均勻執(zhí)行)。
下圖描繪出了幾種動畫模式的關系(橫坐標是運行時間箱舞,縱坐標是動畫屬性[例如位置遍坟、透明度等]):
實際開發(fā)中一個物體的運動往往是復合運動,單一屬性的運動情況比較少晴股,但恰恰屬性動畫每次進行動畫設置時一次只能設置一個屬性進行動畫控制(不管是基礎動畫還是關鍵幀動畫都是如此)愿伴,這樣一來要做一個復合運動的動畫就必須創(chuàng)建多個屬性動畫進行組合。對于一兩種動畫的組合或許處理起來還比較容易电湘,但是對于更多動畫的組合控制往往會變得很麻煩隔节,動畫組的產(chǎn)生就是基于這樣一種情況而產(chǎn)生的万搔。動畫組是一系列動畫的組合,凡是添加到動畫組中的動畫都受控于動畫組官帘,這樣一來各類動畫公共的行為就可以統(tǒng)一進行控制而不必單獨設置瞬雹,而且放到動畫組中的各個動畫可以并發(fā)執(zhí)行,共同構建出復雜的動畫效果刽虹。
動畫組使用起來并不復雜酗捌,首先單獨創(chuàng)建單個動畫(可以是基礎動畫也可以是關鍵幀動畫),然后將基礎動畫添加到動畫組涌哲,最后將動畫組添加到圖層即可胖缤。
前面關鍵幀動畫部分,路徑動畫看起來效果雖然很流暢阀圾,但是落花本身的旋轉運動沒有了哪廓,這里不妨將基礎動畫部分的旋轉動畫和路徑關鍵幀動畫進行組合使得整個動畫看起來更加的和諧、順暢初烘。
//
//? 動畫組
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
CALayer *_layer;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景(注意這個圖片其實在根圖層)UIImage *backgroundImage=[UIImage imageNamed:@"background.jpg"];
self.view.backgroundColor=[UIColor colorWithPatternImage:backgroundImage];//自定義一個圖層_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)建動畫[self groupAnimation];
}#pragmamark 基礎旋轉動畫
-(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"];returnbasicAnimation;
}#pragmamark 關鍵幀移動動畫
-(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"];returnkeyframeAnimation;
}#pragmamark 創(chuàng)建動畫組
-(void)groupAnimation{//1.創(chuàng)建動畫組CAAnimationGroup *animationGroup=[CAAnimationGroup animation];//2.設置組中的動畫和其他屬性CABasicAnimation *basicAnimation=[self rotationAnimation];
CAKeyframeAnimation *keyframeAnimation=[self translationAnimation];
animationGroup.animations=@[basicAnimation,keyframeAnimation];
animationGroup.delegate=self;
animationGroup.duration=10.0;//設置動畫時間涡真,如果動畫組中動畫已經(jīng)設置過動畫屬性則不再生效animationGroup.beginTime=CACurrentMediaTime()+5;//延遲五秒執(zhí)行
//3.給圖層添加動畫[_layer addAnimation:animationGroup forKey:nil];
}#pragmamark - 代理方法#pragmamark 動畫完成
-(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];//設置動畫最終狀態(tài)_layer.position=endPoint;
_layer.transform=CATransform3DMakeRotation(toValue, 0, 0, 1);
[CATransaction commit];
}
@end
轉場動畫就是從一個場景以動畫的形式過渡到另一個場景。轉場動畫的使用一般分為以下幾個步驟:
1.創(chuàng)建轉場動畫
2.設置轉場類型肾筐、子類型(可選)及其他屬性
3.設置轉場后的新視圖并添加動畫到圖層
下表列出了常用的轉場類型(注意私有API是蘋果官方?jīng)]有公開的動畫類型哆料,但是目前通過仍然可以使用):
動畫類型說明對應常量是否支持方向設置
公開API
fade淡出效果kCATransitionFade是
movein新視圖移動到舊視圖上kCATransitionMoveIn是
push新視圖推出舊視圖kCATransitionPush是
reveal移開舊視圖顯示新視圖kCATransitionReveal是
私有API私有API只能通過字符串訪問
cube立方體翻轉效果無是
oglFlip翻轉效果無是
suckEffect收縮效果無否
rippleEffect水滴波紋效果無否
pageCurl向上翻頁效果無是
pageUnCurl向下翻頁效果無是
cameralIrisHollowOpen攝像頭打開效果無否
cameraIrisHollowClose攝像頭關閉效果無否
另外對于支持方向設置的動畫類型還包含子類型:
動畫子類型說明
kCATransitionFromRight從右側轉場
kCATransitionFromLeft從左側轉場
kCATransitionFromTop從頂部轉場
kCATransitionFromBottom從底部轉場
在前面的文章“IOS開發(fā)系列--無限循環(huán)的圖片瀏覽器”中為了使用UIScrollView做無限循環(huán)圖片瀏覽器花費了不少時間在性能優(yōu)化上面,這里使用轉場動畫利用一個UIImageView實現(xiàn)一個漂亮的無限循環(huán)圖片瀏覽器吗铐。
//
//? KCMainViewController.m
//? TransitionAnimation
//
//? Created by Kenshin Cui on 14-3-12.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#defineIMAGE_COUNT 5
@interfaceKCMainViewController (){
UIImageView *_imageView;int_currentIndex;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//定義圖片控件_imageView=[[UIImageView alloc]init];
_imageView.frame=[UIScreen mainScreen].applicationFrame;
_imageView.contentMode=UIViewContentModeScaleAspectFit;
_imageView.image=[UIImage imageNamed:@"0.jpg"];//默認圖片[self.view addSubview:_imageView];//添加手勢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];
}#pragmamark 向左滑動瀏覽下一張圖片
-(void)leftSwipe:(UISwipeGestureRecognizer *)gesture{
[self transitionAnimation:YES];
}#pragmamark 向右滑動瀏覽上一張圖片
-(void)rightSwipe:(UISwipeGestureRecognizer *)gesture{
[self transitionAnimation:NO];
}#pragmamark 轉場動畫
-(void)transitionAnimation:(BOOL)isNext{//1.創(chuàng)建轉場動畫對象CATransition *transition=[[CATransition alloc]init];//2.設置動畫類型,注意對于蘋果官方?jīng)]公開的動畫類型只能使用字符串东亦,并沒有對應的常量定義transition.type=@"cube";//設置子類型if(isNext) {
transition.subtype=kCATransitionFromRight;
}else{
transition.subtype=kCATransitionFromLeft;
}//設置動畫時常transition.duration=1.0f;//3.設置轉場后的新視圖添加轉場動畫_imageView.image=[self getImage:isNext];
[_imageView.layer addAnimation:transition forKey:@"KCTransitionAnimation"];
}#pragmamark 取得當前圖片
-(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
運行效果:
代碼十分簡單,但是效果和性能卻很驚人唬渗。當然演示代碼有限典阵,其他動畫類型大家可以自己實現(xiàn),效果都很絢麗镊逝。
前面介紹了核心動畫中大部分動畫類型壮啊,但是做過動畫處理的朋友都知道,在動畫制作中還有一種動畫類型“逐幀動畫”蹋半。說到逐幀動畫相信很多朋友第一個想到的就是UIImageView他巨,通過設置UIImageView的animationImages屬性充坑,然后調用它的startAnimating方法去播放這組圖片减江。當然這種方法在某些場景下是可以達到逐幀的動畫效果,但是它也存在著很大的性能問題捻爷,并且這種方法一旦設置完圖片中間的過程就無法控制了辈灼。當然,也許有朋友會想到利用iOS的定時器NSTimer定時更新圖片來達到逐幀動畫的效果也榄。這種方式確實可以解決UIImageView一次性加載大量圖片的問題巡莹,而且讓播放過程可控司志,唯一的缺點就是定時器方法調用有時可能會因為當前系統(tǒng)執(zhí)行某種比較占用時間的任務造成動畫連續(xù)性出現(xiàn)問題。
雖然在核心動畫沒有直接提供逐幀動畫類型降宅,但是卻提供了用于完成逐幀動畫的相關對象CADisplayLink骂远。CADisplayLink是一個計時器,但是同NSTimer不同的是腰根,CADisplayLink的刷新周期同屏幕完全一致激才。例如在iOS中屏幕刷新周期是60次/秒,CADisplayLink刷新周期同屏幕刷新一致也是60次/秒额嘿,這樣一來使用它完成的逐幀動畫(又稱為“時鐘動畫”)完全感覺不到動畫的停滯情況瘸恼。
在iOS開篇“IOS開發(fā)系列--IOS程序開發(fā)概覽”中就曾說過:iOS程序在運行后就進入一個消息循環(huán)中(這個消息循環(huán)稱為“主運行循環(huán)”),整個程序相當于進入一個死循環(huán)中册养,始終等待用戶輸入东帅。將CADisplayLink加入到主運行循環(huán)隊列后,它的時鐘周期就和主運行循環(huán)保持一致球拦,而主運行循環(huán)周期就是屏幕刷新周期靠闭。在CADisplayLink加入到主運行循環(huán)隊列后就會循環(huán)調用目標方法,在這個方法中更新視圖內容就可以完成逐幀動畫坎炼。
當然這里不得不強調的是逐幀動畫性能勢必較低阎毅,但是對于一些事物的運動又不得不選擇使用逐幀動畫,例如人的運動点弯,這是一個高度復雜的運動扇调,基本動畫、關鍵幀動畫是不可能解決的抢肛。所大家一定要注意在循環(huán)方法中盡可能的降低算法復雜度狼钮,同時保證循環(huán)過程中內存峰值盡可能低。下面以一個魚的運動為例為大家演示一下逐幀動畫捡絮。
//
//? KCMainViewController.m
//? DisplayLink
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#defineIMAGE_COUNT 10
@interfaceKCMainViewController (){
CALayer *_layer;int_index;
NSMutableArray *_images;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景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];//由于魚的圖片在循環(huán)中會不斷創(chuàng)建熬芜,而10張魚的照片相對都很小
//與其在循環(huán)中不斷創(chuàng)建UIImage不如直接將10張圖片緩存起來_images=[NSMutableArrayarray];for(inti=0; i<10; ++i) {
NSString *imageName=[NSString stringWithFormat:@"fish%i.png",i];
UIImage *image=[UIImage imageNamed:imageName];
[_images addObject:image];
}//定義時鐘對象CADisplayLink *displayLink=[CADisplayLink displayLinkWithTarget:self selector:@selector(step)];//添加時鐘對象到主運行循環(huán)[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}#pragmamark 每次屏幕刷新就會執(zhí)行一次此方法(每秒接近60次)
-(void)step{//定義一個變量記錄執(zhí)行次數(shù)static ints=0;//每秒執(zhí)行6次if(++s%10==0) {
UIImage *image=_images[_index];
_layer.contents=(id)image.CGImage;//更新圖片_index=(_index+1)%IMAGE_COUNT;
}
}
@end
運行效果:
注意:上面僅僅演示了逐幀動畫的過程,事實上結合其他動畫類型可以讓整條魚游動起來福稳,這里不再贅述涎拉。
有了前面核心動畫的知識,相信大家開發(fā)出一般的動畫效果應該不在話下的圆。在核心動畫開篇也給大家說過鼓拧,其實UIView本身對于基本動畫和關鍵幀動畫、轉場動畫都有相應的封裝越妈,在對動畫細節(jié)沒有特殊要求的情況下使用起來也要簡單的多季俩。可以說在日常開發(fā)中90%以上的情況使用UIView的動畫封裝方法都可以搞定梅掠,因此在熟悉了核心動畫的原理之后還是有必要給大家簡單介紹一下UIView中各類動畫使用方法的酌住。由于前面核心動畫內容已經(jīng)進行過詳細介紹店归,學習UIView的封裝方法根本是小菜一碟,這里對于一些細節(jié)就不再贅述了酪我。
基礎動畫部分和前面的基礎動畫演示相對應消痛,演示點擊屏幕落葉飄落到鼠標點擊位置的過程。注意根據(jù)前面介紹的隱式動畫知識其實非根圖層直接設置終點位置不需要使用UIView的動畫方法也可以實現(xiàn)動畫效果都哭,因此這里落花不再放到圖層中而是放到了一個UIImageView中肄满。
下面的代碼演示了通過block和靜態(tài)方法實現(xiàn)動畫控制的過程:
//
//? UIView實現(xiàn)基礎動畫
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
UIImageView *_imageView;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景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];
}#pragmamark 點擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];//方法1:block方式
/*開始動畫,UIView的動畫方法執(zhí)行完后動畫會停留在重點位置质涛,而不需要進行任何特殊處理
duration:執(zhí)行時間
delay:延遲時間
options:動畫設置稠歉,例如自動恢復、勻速運動等
completion:動畫完成回調方法
*/
//? ? [UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
//? ? ? ? _imageView.center=location;
//? ? } completion:^(BOOL finished) {
//? ? ? ? NSLog(@"Animation end.");
//? ? }];
//方法2:靜態(tài)方法
//開始動畫[UIView beginAnimations:@"KCBasicAnimation"context:nil];
[UIView setAnimationDuration:5.0];//[UIView setAnimationDelay:1.0];//設置延遲
//[UIView setAnimationRepeatAutoreverses:NO];//是否回復
//[UIView setAnimationRepeatCount:10];//重復次數(shù)
//[UIView setAnimationStartDate:(NSDate *)];//設置動畫開始運行的時間
//[UIView setAnimationDelegate:self];//設置代理
//[UIView setAnimationWillStartSelector:(SEL)];//設置動畫開始運動的執(zhí)行方法
//[UIView setAnimationDidStopSelector:(SEL)];//設置動畫運行結束后的執(zhí)行方法_imageView.center=location;//開始動畫[UIView commitAnimations];
}
@end
補充--彈簧動畫效果
由于在iOS開發(fā)中彈性動畫使用很普遍汇陆,所以在iOS7蘋果官方直接提供了一個方法用于彈性動畫開發(fā)怒炸,下面簡單的演示一下:
//
//? UIView實現(xiàn)基礎動畫--彈性動畫
//? Animation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
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];
}#pragmamark 點擊事件
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch=touches.anyObject;
CGPoint location= [touch locationInView:self.view];/*創(chuàng)建彈性動畫
damping:阻尼,范圍0-1毡代,阻尼越接近于0阅羹,彈性效果越明顯
velocity:彈性復位的速度
*/[UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{
_imageView.center=location;//CGPointMake(160, 284);} completion:nil];
}
@end
補充--動畫設置參數(shù)
在動畫方法中有一個option參數(shù),UIViewAnimationOptions類型教寂,它是一個枚舉類型捏鱼,動畫參數(shù)分為三類,可以組合使用:
1.常規(guī)動畫屬性設置(可以同時選擇多個進行設置)
UIViewAnimationOptionLayoutSubviews:動畫過程中保證子視圖跟隨運動酪耕。
UIViewAnimationOptionAllowUserInteraction:動畫過程中允許用戶交互导梆。
UIViewAnimationOptionBeginFromCurrentState:所有視圖從當前狀態(tài)開始運行。
UIViewAnimationOptionRepeat:重復運行動畫迂烁。
UIViewAnimationOptionAutoreverse :動畫運行到結束點后仍然以動畫方式回到初始點看尼。
UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套動畫時間設置。
UIViewAnimationOptionOverrideInheritedCurve:忽略嵌套動畫速度設置盟步。
UIViewAnimationOptionAllowAnimatedContent:動畫過程中重繪視圖(注意僅僅適用于轉場動畫)藏斩。
UIViewAnimationOptionShowHideTransitionViews:視圖切換時直接隱藏舊視圖、顯示新視圖却盘,而不是將舊視圖從父視圖移除(僅僅適用于轉場動畫)
UIViewAnimationOptionOverrideInheritedOptions :不繼承父動畫設置或動畫類型狰域。
2.動畫速度控制(可從其中選擇一個設置)
UIViewAnimationOptionCurveEaseInOut:動畫先緩慢,然后逐漸加速黄橘。
UIViewAnimationOptionCurveEaseIn :動畫逐漸變慢兆览。
UIViewAnimationOptionCurveEaseOut:動畫逐漸加速。
UIViewAnimationOptionCurveLinear :動畫勻速執(zhí)行旬陡,默認值拓颓。
3.轉場類型(僅適用于轉場動畫設置语婴,可以從中選擇一個進行設置描孟,基本動畫、關鍵幀動畫不需要設置)
UIViewAnimationOptionTransitionNone:沒有轉場動畫效果。
UIViewAnimationOptionTransitionFlipFromLeft :從左側翻轉效果歇由。
UIViewAnimationOptionTransitionFlipFromRight:從右側翻轉效果萍肆。
UIViewAnimationOptionTransitionCurlUp:向后翻頁的動畫過渡效果。
UIViewAnimationOptionTransitionCurlDown :向前翻頁的動畫過渡效果廉羔。
UIViewAnimationOptionTransitionCrossDissolve:舊視圖溶解消失顯示下一個新視圖的效果溉痢。
UIViewAnimationOptionTransitionFlipFromTop :從上方翻轉效果。
UIViewAnimationOptionTransitionFlipFromBottom:從底部翻轉效果憋他。
從iOS7開始UIView動畫中封裝了關鍵幀動畫孩饼,下面就來看一下如何使用UIView封裝方法進行關鍵幀動畫控制,這里實現(xiàn)前面關鍵幀動畫部分對于落花的控制竹挡。
//
//? UIView關鍵幀動畫
//? UIViewAnimation
//
//? Created by Kenshin Cui on 14-3-22.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"@interfaceKCMainViewController (){
UIImageView *_imageView;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//設置背景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];
/*關鍵幀動畫
options:
*/[UIView animateKeyframesWithDuration:5.0 delay:0 options: UIViewAnimationOptionCurveLinear| UIViewAnimationOptionCurveLinear animations:^{//第二個關鍵幀(準確的說第一個關鍵幀是開始位置):從0秒開始持續(xù)50%的時間镀娶,也就是5.0*0.5=2.5秒[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
_imageView.center=CGPointMake(80.0, 220.0);
}];//第三個關鍵幀,從0.5*5.0秒開始揪罕,持續(xù)5.0*0.25=1.25秒[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{
_imageView.center=CGPointMake(45.0, 300.0);
}];//第四個關鍵幀:從0.75*5.0秒開始梯码,持所需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
補充--動畫設置參數(shù)
對于關鍵幀動畫也有一些動畫參數(shù)設置options,UIViewKeyframeAnimationOptions類型好啰,和上面基本動畫參數(shù)設置有些差別轩娶,關鍵幀動畫設置參數(shù)分為兩類,可以組合使用:
1.常規(guī)動畫屬性設置(可以同時選擇多個進行設置)
UIViewAnimationOptionLayoutSubviews:動畫過程中保證子視圖跟隨運動框往。
UIViewAnimationOptionAllowUserInteraction:動畫過程中允許用戶交互鳄抒。
UIViewAnimationOptionBeginFromCurrentState:所有視圖從當前狀態(tài)開始運行。
UIViewAnimationOptionRepeat:重復運行動畫椰弊。
UIViewAnimationOptionAutoreverse :動畫運行到結束點后仍然以動畫方式回到初始點嘁酿。
UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套動畫時間設置。
UIViewAnimationOptionOverrideInheritedOptions :不繼承父動畫設置或動畫類型男应。
2.動畫模式設置(同前面關鍵幀動畫動畫模式一一對應闹司,可以從其中選擇一個進行設置)
UIViewKeyframeAnimationOptionCalculationModeLinear:連續(xù)運算模式。
UIViewKeyframeAnimationOptionCalculationModeDiscrete :離散運算模式沐飘。
UIViewKeyframeAnimationOptionCalculationModePaced:均勻執(zhí)行運算模式游桩。
UIViewKeyframeAnimationOptionCalculationModeCubic:平滑運算模式。
UIViewKeyframeAnimationOptionCalculationModeCubicPaced:平滑均勻運算模式耐朴。
注意:前面說過關鍵幀動畫有兩種形式借卧,上面演示的是屬性值關鍵幀動畫,路徑關鍵幀動畫目前UIView還不支持筛峭。
從iOS4.0開始铐刘,UIView直接封裝了轉場動畫,使用起來同樣很簡單影晓。
//
//? UIView轉場動畫
//? TransitionAnimation
//
//? Created by Kenshin Cui on 14-3-12.
//? Copyright (c) 2014年 Kenshin Cui. All rights reserved.
//#import"KCMainViewController.h"#defineIMAGE_COUNT 5
@interfaceKCMainViewController (){
UIImageView *_imageView;int_currentIndex;
}
@end
@implementation KCMainViewController
- (void)viewDidLoad {
[super viewDidLoad];//定義圖片控件_imageView=[[UIImageView alloc]init];
_imageView.frame=[UIScreen mainScreen].applicationFrame;
_imageView.contentMode=UIViewContentModeScaleAspectFit;
_imageView.image=[UIImage imageNamed:@"0.jpg"];//默認圖片[self.view addSubview:_imageView];//添加手勢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];
}#pragmamark 向左滑動瀏覽下一張圖片
-(void)leftSwipe:(UISwipeGestureRecognizer *)gesture{
[self transitionAnimation:YES];
}#pragmamark 向右滑動瀏覽上一張圖片
-(void)rightSwipe:(UISwipeGestureRecognizer *)gesture{
[self transitionAnimation:NO];
}#pragmamark 轉場動畫
-(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];
}#pragmamark 取得當前圖片
-(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
上面的轉場動畫演示中镰吵,其實僅僅有一個視圖UIImageView做轉場動畫檩禾,每次轉場通過切換UIImageView的內容而已。如果有兩個完全不同的視圖疤祭,并且每個視圖布局都很復雜盼产,此時要在這兩個視圖之間進行轉場可以使用+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(4_0)方法進行兩個視圖間的轉場,需要注意的是默認情況下轉出的視圖會從父視圖移除勺馆,轉入后重新添加戏售,可以通過UIViewAnimationOptionShowHideTransitionViews參數(shù)設置,設置此參數(shù)后轉出的視圖會隱藏(不會移除)轉入后再顯示草穆。
注意:轉場動畫設置參數(shù)完全同基本動畫參數(shù)設置灌灾;同直接使用轉場動畫不同的是使用UIView的裝飾方法進行轉場動畫其動畫效果較少,因為這里無法直接使用私有API悲柱。