UI部分-iOS動(dòng)畫

iOS核心動(dòng)畫-Core Animation

概論

目標(biāo):1. 學(xué)會(huì)使用圖層精簡(jiǎn)非交互式繪圖;2. 通過(guò)核心動(dòng)畫創(chuàng)建基礎(chǔ)動(dòng)畫,關(guān)鍵幀動(dòng)畫,動(dòng)畫組,轉(zhuǎn)場(chǎng)動(dòng)畫;3. 學(xué)習(xí)使用UIView對(duì)這些動(dòng)畫簡(jiǎn)化操作的裝飾方法.

1. CALayer

1.1 簡(jiǎn)介.

CALyer包含在QuartzCore框架中,這是一個(gè)跨平臺(tái)的框架(iOS 和 MAC OSX);在使用Core Animation開(kāi)發(fā)動(dòng)畫的本質(zhì)就是將CALayer中的內(nèi)容轉(zhuǎn)化為位圖從而供硬件操作;

在Core Animation中我們更多是直接操作圖層;UIView中有一個(gè)layer屬性作為根圖層,根圖層上可以放其他子圖層,UIView中所有能看到的內(nèi)容都包含在layer中;

1.2 CALayer常用屬性.

iOS中CALayer的設(shè)計(jì)主要是為了內(nèi)容展示和動(dòng)畫操作,它本身并不包含在UIKit中,所以不能響應(yīng)事件.CALyer設(shè)計(jì)之初就考慮它的動(dòng)畫操作功能,因此它的很多屬性在修改時(shí)都能形成動(dòng)畫效果,這種屬性稱為"隱式動(dòng)畫屬性",很多開(kāi)發(fā)的時(shí)候做平移動(dòng)畫直接使用隱式動(dòng)畫即可;

但是,對(duì)于UIView的根圖層而言,屬性修改不會(huì)形成動(dòng)畫效果,因?yàn)楹芏嗲闆r下根圖層是充當(dāng)容器的作用,如果的它的屬性變動(dòng)形成的動(dòng)畫效果會(huì)直接影響子圖層.而且根視圖的創(chuàng)建工作完全由系統(tǒng)完成,無(wú)法重新創(chuàng)建,只能往根視圖中添加或移除子圖層.層級(jí)結(jié)構(gòu)如下:


圖片名稱

列表CALyer的常用屬性如下:

圖片名稱
  • 隱式動(dòng)畫的本質(zhì)是這些屬性的變動(dòng)默認(rèn)隱含了CABasicAnimation動(dòng)畫實(shí)現(xiàn).參考Xcode文檔“Animatable Properties”一節(jié)
  • CALayer中很少使用frame屬性,應(yīng)為frame本身不支持動(dòng)畫效果,使用bounds和position代替.
  • CALayer中使用opacity表示透明度;
  • anchorPoint是圖層錨點(diǎn): 決定CALayer身上哪個(gè)點(diǎn)在position屬性所指的位置 .以自身左上角為原點(diǎn),范圍0到1;默認(rèn)是[0.5,0.5];
  • (面試題)CALayer為是么使用CGImageRef和CGColorRef?--> QuartzCore是跨平臺(tái)的框架,所以不能使用UIKit中的UIColor;
  • 默認(rèn)每個(gè)動(dòng)畫都會(huì)包裝一個(gè)事務(wù),因此執(zhí)行隱式動(dòng)畫之前可以設(shè)置這個(gè)動(dòng)畫時(shí)長(zhǎng): [CATransaction setAnimation:1]

擴(kuò)展:若有特殊需求,要關(guān)閉隱式動(dòng)畫-需要用到動(dòng)畫事務(wù)CATransaction,在事務(wù)內(nèi)將隱式動(dòng)畫關(guān)閉

//開(kāi)啟事務(wù)
[CATransaction begin];
//禁用隱式動(dòng)畫
[CATransaction setDisableActions:YES];

在這部分更改圖層屬性.將不會(huì)產(chǎn)生隱式動(dòng)畫

//提交事務(wù)
[CATransaction commit];

1.3 CALayer繪圖

上一章中使用的Quartz 2D繪圖其實(shí)已經(jīng)用到了CALayer,當(dāng)利用drawRect:方法繪圖的本質(zhì)就是繪制到圖層中,只是drawRect: 是由UIKit組件進(jìn)行調(diào)用,所以里面可以使用一些UIKit封裝的方法進(jìn)行繪圖;

而直接繪制到圖層的方法由于并非UIKit直接調(diào)用,因此只能用原生的Core Graphics方法繪制.
圖層繪圖有兩種方法,不管哪種繪制完成都必須調(diào)用圖層的setNeedDisplay方法 (注意:圖層的方法,不是上一章UIView的方法.)

  1. 通過(guò)圖層代理 drawLayer: inContext: 方法.
  2. 通過(guò)自定義圖層的 drawInContext: 方法;
1.3.1 代理方法繪圖

步驟:

  1. 指定圖層的代理,所有NSObject都遵守協(xié)議,所以不用手動(dòng)實(shí)現(xiàn)CALayerDelegate;
  2. 在代理對(duì)象中重寫-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
  3. 調(diào)用圖層的setNeedsDisplay方法;

代碼:

#define PHOTO_HEIGHT 50

//自定義圖層
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 = 50/2;
//僅僅設(shè)置圓角,對(duì)于圖形而言可以正常顯示,但是對(duì)于圖層中繪制的圖片無(wú)法正常顯示,需要:
layer.masksToBounds = YES;  來(lái)裁剪超出圖層部分 才能顯示出圓角;
//但是同理:陰影效果無(wú)法和masksToBounds 同時(shí)使用.陰影也會(huì)被剪掉;

//設(shè)置邊框
layer.borderColor = [UIColor whiteColor].CGColor;
layer.borderWidth = 2;
//添加到根圖層
[self.view.layer addSublayer: layer ]

//1. 設(shè)置圖層代理
layer.delegate = self;

//2. 調(diào)用
[layer setNeedsDisplay];



//3. 實(shí)現(xiàn)代理方法
- (void) -(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
// 傳入的layer是上面定義的圖層   圖形上下文也是此圖層的上下文.
//保存繪圖狀態(tài)
CGContestSaveGState(cox);

//圖形上下文形變,解決圖片倒立 問(wèn)題 //UIKit 坐標(biāo)系與 Core Graphics 坐標(biāo)系不同;
CGContextScaleCTM (ctx, 1, -1);
CGContextTranslateCTM(cox, 0, -PHOTO_HEIGHT);

UIImage *image = [UIImage imageName:@"photo.png"];
//繪圖      注意:這個(gè)位置時(shí)相對(duì)于圖層  不是屏幕;
CGContextDrawImage(ctx, CGRectMake(0, 0, PHOTO_HEIGHT, PHOTO_HEIGHT), image.CGImage);
 
CGContextRestoreGState(ctx);
}

擴(kuò)展1.帶陰影效果的圖片裁剪:

由于上面的矛盾,兩個(gè)設(shè)置不能同時(shí)顯示;換個(gè)思路:使用兩個(gè)圖層,下面的用來(lái)繪制陰影作為容器圖層,上面的用來(lái)顯示圖片,裁剪上面的即可;

擴(kuò)展2.圖層的形變:

  1. 從上面代碼中大家不難發(fā)現(xiàn)使用Core Graphics繪制圖片時(shí)會(huì)倒立顯示,上面是使用圖形上下文形變來(lái)解決這個(gè)問(wèn)題.其實(shí)設(shè)置繞x軸旋轉(zhuǎn)180度同樣可以達(dá)到正確顯示目的,只是用圖形上下文做不到旋轉(zhuǎn);
  2. 但是其實(shí)圖層有個(gè)transform屬性可以直接設(shè)置旋轉(zhuǎn),不需要借助圖形上下文,需要注意的是transform是CATransform3D類型,形變可以在三個(gè)維度上進(jìn)行;(如:CATransform3DMakeTranslation( )、CATransform3DMakeScale( )、CATransform3DMakeRotation( ))。//利用圖層形變解決圖像倒立問(wèn)題 layer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
  3. layer默認(rèn)照錨點(diǎn)旋轉(zhuǎn), 3D向量旋轉(zhuǎn)CATransFrom3DMakeRotation; 后三個(gè)是三維坐標(biāo)(x,y,z); xy面作為手機(jī)屏幕面.
  4. 既然如此,為什么還要說(shuō)圖層的形變,直接設(shè)置transform不就行了么;因?yàn)樾巫儗?duì)于動(dòng)畫有特殊意義.在動(dòng)畫開(kāi)發(fā)中形變往往不是直接設(shè)置transform,而是通過(guò)keyPath設(shè)置,這KVC設(shè)置方法和前面沒(méi)有區(qū)別,但是這種方式在動(dòng)畫中很實(shí)用,因?yàn)樗梢院芊奖愕膶追N形變組合到一起使用, 例:[layer setValue:@M_PI forKeyPath:@"transform.rotation.x"];具體屬性可查詢文檔“CATransform3D Key Paths”一節(jié).
1.3.2 使用自定義圖層繪圖

步驟:

  1. 自定義繼承CALyer的類,
  2. 在這個(gè)類.m中實(shí)現(xiàn)drawInContext: 方法 進(jìn)行繪圖;
  3. 在UIView中新建這個(gè)類對(duì)象 調(diào)用圖層 setNeedsDisplay即可顯示出繪圖;

如下:

//1. 創(chuàng)建繼承CALayer的KALayer類;
//2. 在KALayer中重寫drawInContext:(CGContextRef)ctx  進(jìn)行具體繪圖 
-(void)drawInContext:(CGContextRef)ctx{
//打印一下上下文地址
    NSLog(@"CGContext:%@",ctx);


//// 開(kāi)始畫圖
    CGContextMoveToPoint(ctx, 94.5, 33.5);
    CGContextAddLineToPoint(ctx,104.02, 47.39);        
    CGContextClosePath(ctx);

    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);

    CGContextDrawPath(ctx, kCGPathFillStroke);  
}
    
//3. 在view文件中.
-(instancetype)initWithFrame:(CGRect)frame{
NSLog(@"initWithFrame:");
if (self=[super initWithFrame:frame]) {
    KCLayer *layer=[[KCLayer alloc]init];
    //確定layer位置大小即可
    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];
    }
return self;
}
 
 //驗(yàn)證 :drawRect:方法繪圖的本質(zhì)就是繪制到圖層中;
//這里能調(diào)用是應(yīng)為UIView創(chuàng)建圖層會(huì)自動(dòng)設(shè)置圖層代理為其自身
- (void)drawLayer:(CALayer *)layer inContext(CGContextRef)ctx{
    [super drawLayer:layer inContext:ctx];
    //打印上下文
    NSLog(@"CGContext:%@",ctx);
}

- (void)drawRect:(CGRect) rect {
    [super drawRect:rect]
    // 我們?cè)谶@里獲取到的當(dāng)前圖形上下文正是drawLayer:中傳遞的
    NSLog(@"CGContext:%@",UIGraphicsGetCurrentContext())
}

UIView在顯示時(shí)其根圖層會(huì)自動(dòng)創(chuàng)建一個(gè)CGContextRef(CALayer本質(zhì)使用的是位圖上下文),同時(shí)調(diào)用圖層代理(UIView創(chuàng)建圖層會(huì)自動(dòng)設(shè)置圖層代理為其自身,引申的,不能給給自定義layer的代理設(shè)為view,應(yīng)為view已經(jīng)做自己根圖層的代理了)的draw: inContext:方法并將圖形上下文作為參數(shù)傳遞給這個(gè)方法。而在UIView的draw:inContext:方法中會(huì)調(diào)用其drawRect:方法,在drawRect:方法中使用UIGraphicsGetCurrentContext()方法得到的上下文正是前面創(chuàng)建的上下文。


2. Core Animation 核心動(dòng)畫

簡(jiǎn)介.

在iOS中實(shí)現(xiàn)一個(gè)動(dòng)畫相當(dāng)簡(jiǎn)單,只要調(diào)用UIView的塊代碼即可實(shí)現(xiàn)一個(gè)動(dòng)畫效果,使用上面UIView封裝的方法進(jìn)行動(dòng)畫設(shè)置固然十分方便,但是具體動(dòng)畫如何實(shí)現(xiàn)我們是不清楚的,而且上面的代碼還有一些問(wèn)題是無(wú)法解決的,例如:如何控制動(dòng)畫的暫停?如何進(jìn)行動(dòng)畫的組合?。。。這就需要了解核心動(dòng)畫.

0082-640x357.png

核動(dòng)畫分類:

  1. CAAnimation: 核心動(dòng)畫基礎(chǔ)類,不直接使用;負(fù)責(zé)動(dòng)畫 運(yùn)行時(shí)間,速度的控制 ,本身實(shí)現(xiàn)了CAMediaTiming協(xié)議;
    1. CAPropertyAnimation: 隱式動(dòng)畫的基類,不能直接使用;
      • CABasicAnimation:基礎(chǔ)動(dòng)畫,通過(guò)屬性修改進(jìn)行動(dòng)畫參數(shù)控制,只有初始狀態(tài)和結(jié)束狀態(tài)。
      • CAKeyframeAnimation:關(guān)鍵幀動(dòng)畫,同樣是通過(guò)屬性進(jìn)行動(dòng)畫參數(shù)控制,但是同基礎(chǔ)動(dòng)畫不同的是它可以有多個(gè)狀態(tài)控制。
    2. CAAnimationGroup:動(dòng)畫組,動(dòng)畫組是一種組合模式設(shè)計(jì),可以通過(guò)動(dòng)畫組來(lái)進(jìn)行所有動(dòng)畫行為的統(tǒng)一控制,組中所有動(dòng)畫效果可以并發(fā)執(zhí)行。
    3. CATransition:轉(zhuǎn)場(chǎng)動(dòng)畫,主要通過(guò)濾鏡進(jìn)行動(dòng)畫效果設(shè)置。

基礎(chǔ)動(dòng)畫/關(guān)鍵幀動(dòng)畫都是屬于屬性動(dòng)畫,開(kāi)發(fā)人員只需要設(shè)置初始值和結(jié)束值,中間的過(guò)程動(dòng)畫(又叫“補(bǔ)間動(dòng)畫”)由系統(tǒng)自動(dòng)計(jì)算產(chǎn)生。和基礎(chǔ)動(dòng)畫不同的是關(guān)鍵幀動(dòng)畫可以設(shè)置多個(gè)屬性值,每?jī)蓚€(gè)屬性中間的補(bǔ)間動(dòng)畫由系統(tǒng)自動(dòng)完成,因此從這個(gè)角度而言基礎(chǔ)動(dòng)畫又可以看成是只有兩個(gè)關(guān)鍵幀的關(guān)鍵幀動(dòng)畫。

2.1 基礎(chǔ)動(dòng)畫CABasicAnimation.

很多情況通過(guò)基礎(chǔ)動(dòng)畫就能滿足要求.如果不使用UIView的封裝后方法,一般步驟:

  1. 初始化動(dòng)畫并指定動(dòng)畫屬性;決定了執(zhí)行怎樣的動(dòng)畫,是調(diào)整哪個(gè)屬性來(lái)執(zhí)行動(dòng)畫;
  2. 設(shè)置動(dòng)畫初始值結(jié)束值和其他屬性.
    • 初始值一般省略,默認(rèn)圖層初始狀態(tài).
    • 一般使用KVC,[self.redView.layer setValue:@0.5 forKeyPath:@"transform.scale"];
    • 其他屬性:duration時(shí)間,repeatCount次數(shù)(設(shè)為HUGE_VALF表示循環(huán)動(dòng)畫效果);
  3. 添加動(dòng)畫到圖層.

示例代碼如下:

//1.創(chuàng)建動(dòng)畫并指定動(dòng)畫屬性
CABasicAnimation *basicAnimation=[CABasicAnimation animationWithKeyPath:@"position"];

//2.設(shè)置動(dòng)畫屬性初始值和結(jié)束值 
//    basicAnimation.fromValue=[NSNumber numberWithInteger:50];//可以不設(shè)置,默認(rèn)為圖層初始狀態(tài)
//toValue表示最終到哪個(gè)值    byValue 相對(duì)于上一次增加多少值
basicAnimation.toValue=[NSValue valueWithCGPoint:location];

//設(shè)置其他動(dòng)畫屬性
basicAnimation.duration=5.0;//動(dòng)畫時(shí)間5秒
//  basicAnimation.repeatCount=HUGE_VALF;//設(shè)置重復(fù)次數(shù),HUGE_VALF可看做無(wú)窮大,起到循環(huán)動(dòng)畫的效果
//  設(shè)置不反彈-兩者不可缺一 
//  basicAnimation.removedOnCompletion=NO;//運(yùn)行完畢一次后不要移除動(dòng)畫 
//  basicAnimation.fillMode= KCAFillModeForwards  //保存最新模式 

//3.添加動(dòng)畫到圖層,注意key相當(dāng)于給動(dòng)畫進(jìn)行命名,以后獲得該動(dòng)畫時(shí)可以使用此名稱獲取
[_layer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];

存在問(wèn)題:動(dòng)畫結(jié)束后動(dòng)畫圖層回到了原來(lái)的位置,當(dāng)然使用UIView封裝的方法是沒(méi)這個(gè)問(wèn)題的;問(wèn)題的原因:圖層動(dòng)畫的本質(zhì)就是將圖層內(nèi)部的內(nèi)容轉(zhuǎn)化為位圖經(jīng)硬件操作形成一種動(dòng)畫效果,其實(shí)圖層本身并沒(méi)有任何的變化,動(dòng)畫效果是假象;

擴(kuò)展:動(dòng)畫的暫停與恢復(fù)

核心動(dòng)畫的運(yùn)行有一個(gè)媒體時(shí)間的概念:假設(shè)將一個(gè)旋轉(zhuǎn)動(dòng)畫設(shè)置旋轉(zhuǎn)一周要用時(shí)60秒,那么當(dāng)都動(dòng)畫旋轉(zhuǎn)90度后媒體時(shí)間就是15秒. 此時(shí)如果要將動(dòng)畫暫停,只需要讓媒體時(shí)間偏離量設(shè)置為15秒,然后動(dòng)畫運(yùn)行速度設(shè)置為0時(shí)期停止運(yùn)動(dòng);
類似的,如果暫停了50秒后需要恢復(fù)動(dòng)畫(此時(shí)媒體時(shí)間為65秒); 這時(shí)只要將動(dòng)畫開(kāi)始時(shí)間設(shè)置為當(dāng)前媒體時(shí)間65秒減去暫停時(shí)的時(shí)刻(之前的設(shè)置的偏移量)即(75-15 =50),即暫停的時(shí)時(shí)長(zhǎng),與此同時(shí)將偏移量重置為0,運(yùn)行速度設(shè)為1;
這個(gè)過(guò)程中真正起到暫停作用的是動(dòng)畫速度;媒體事件偏移量以及恢復(fù)時(shí)的開(kāi)始事件設(shè)置主要是為了讓動(dòng)畫更加連貫,不會(huì)閃跳;

#pragma mark 動(dòng)畫暫停
-(void)animationPause{
//取得指定圖層動(dòng)畫的媒體時(shí)間,后面參數(shù)用于指定子圖層,這里不需要
    CFTimeInterval interval=[_layer convertTime:CACurrentMediaTime() fromLayer:nil];
//設(shè)置時(shí)間偏移量,保證暫停時(shí)停留在旋轉(zhuǎn)的位置
    [_layer setTimeOffset:interval];
//速度設(shè)置為0,暫停動(dòng)畫
    _layer.speed=0;
}

#pragma mark 動(dòng)畫恢復(fù)
-(void)animationResume{
//獲得暫停的時(shí)間
    CFTimeInterval beginTime= CACurrentMediaTime()- _layer.timeOffset;
//設(shè)置偏移量
    _layer.timeOffset=0;
//設(shè)置開(kāi)始時(shí)間
    _layer.beginTime=beginTime;
//設(shè)置動(dòng)畫速度,開(kāi)始運(yùn)動(dòng)
    _layer.speed=1.0;
}

注意:動(dòng)畫暫停針對(duì)的是圖層而不是圖層中的某個(gè)動(dòng)畫。;


2.2 關(guān)鍵幀動(dòng)畫CAKeyframeAnimation.

使用類似于基礎(chǔ)動(dòng)畫,但可以在無(wú)數(shù)個(gè)值(稱為關(guān)鍵幀)間改變.有個(gè)屬性values數(shù)組,用來(lái)接受這些值;

關(guān)鍵幀動(dòng)畫開(kāi)發(fā)分為兩種形式:一種是通過(guò)設(shè)置不同的屬性值進(jìn)行關(guān)鍵幀控制,另一種是通過(guò)繪制路徑進(jìn)行關(guān)鍵幀控制。后者優(yōu)先級(jí)高于前者,如果設(shè)置了路徑則屬性值就不再起作用。

屬性關(guān)鍵幀

 //1.創(chuàng)建關(guān)鍵幀動(dòng)畫并設(shè)置動(dòng)畫屬性
CAKeyframeAnimation *keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];

//2.設(shè)置關(guān)鍵幀,這里有四個(gè)關(guān)鍵幀
NSValue *key1=[NSValue valueWithCGPoint:_layer.position];//對(duì)于關(guān)鍵幀動(dòng)畫初始值不能省略
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)畫到圖層,添加動(dòng)畫后就會(huì)執(zhí)行動(dòng)畫
[_layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];

路徑關(guān)鍵幀

//1.創(chuàng)建關(guān)鍵幀動(dòng)畫并設(shè)置動(dòng)畫屬性
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()+2;//設(shè)置延遲2秒執(zhí)行


//3.添加動(dòng)畫到圖層,添加動(dòng)畫后就會(huì)執(zhí)行動(dòng)畫
[_layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];

不同動(dòng)畫效果如下:

<img src="http://images.cnitblog.com/blog/62046/201409/150628429879513.png
" width = "120" height = "200" alt="圖片名稱" align=center />
<img src="http://images.cnitblog.com/blog/62046/201409/150628441904812.png
" width = "120" height = "200" alt="路徑幀" align=center />

補(bǔ)充-其他屬性

keyTimes:各個(gè)關(guān)鍵幀的時(shí)間控制,值是各關(guān)鍵幀所占設(shè)置的動(dòng)畫時(shí)間的比例,(可以大于1);

caculationMode:動(dòng)畫計(jì)算模式.拿上面keyValues動(dòng)畫舉例,之所以1到2幀能形成連貫性動(dòng)畫而不是直接從第1幀經(jīng)過(guò)8/3秒到第2幀是因?yàn)閯?dòng)畫模式是連續(xù)的(值為kCAAnimationLinear,這是計(jì)算模式的默認(rèn)值);而如果指定了動(dòng)畫模式為kCAAnimationDiscrete離散的那么你會(huì)看到動(dòng)畫從第1幀經(jīng)過(guò)8/3秒直接到第2幀,中間沒(méi)有任何過(guò)渡。其他動(dòng)畫模式還有:kCAAnimationPaced(均勻執(zhí)行,會(huì)忽略keyTimes)、kCAAnimationCubic(平滑執(zhí)行,對(duì)于位置變動(dòng)關(guān)鍵幀動(dòng)畫運(yùn)行軌跡更平滑)、kCAAnimationCubicPaced(平滑均勻執(zhí)行)。
下圖描繪出了幾種動(dòng)畫模式的關(guān)系(橫坐標(biāo)是運(yùn)行時(shí)間,縱坐標(biāo)是動(dòng)畫屬性[例如位置、透明度等]):
<img src="http://images.cnitblog.com/blog/62046/201409/150628454405097.png
" width = "300" height = "200" alt="圖片名稱" align=center />

2.3 動(dòng)畫組CAAnimationGroup.

實(shí)際開(kāi)發(fā)中一個(gè)物體的運(yùn)動(dòng)往往是復(fù)合運(yùn)動(dòng),單一屬性的運(yùn)動(dòng)情況比較少,但恰恰屬性動(dòng)畫每次進(jìn)行動(dòng)畫設(shè)置時(shí)一次只能設(shè)置一個(gè)屬性進(jìn)行動(dòng)畫控制,這樣一來(lái)要做一個(gè)復(fù)合運(yùn)動(dòng)的動(dòng)畫就必須創(chuàng)建多個(gè)屬性動(dòng)畫進(jìn)行組合。動(dòng)畫組的產(chǎn)生就是基于這樣一種情況而產(chǎn)生的.

動(dòng)畫組是一系列動(dòng)畫的組合,凡是添加到動(dòng)畫組中的動(dòng)畫都受控于動(dòng)畫組,這樣一來(lái)各類動(dòng)畫公共的行為就可以統(tǒng)一進(jìn)行控制而不必單獨(dú)設(shè)置,而且放到動(dòng)畫組中的各個(gè)動(dòng)畫可以并發(fā)執(zhí)行,共同構(gòu)建出復(fù)雜的動(dòng)畫效果。

一般步驟:首先單獨(dú)創(chuàng)建單個(gè)動(dòng)畫(可以是基礎(chǔ)動(dòng)畫也可以是關(guān)鍵幀動(dòng)畫),之后添加到動(dòng)畫組上,再把動(dòng)畫組添加到圖層,就可以統(tǒng)一,同時(shí)執(zhí)行這些動(dòng)畫.

//1.創(chuàng)建動(dòng)畫組
CAAnimationGroup *animationGroup=[CAAnimationGroup animation];

//2.設(shè)置組中的動(dòng)畫和其他屬性
CABasicAnimation *basicAnimation=[self rotationAnimation]; //是封裝上面的基礎(chǔ)動(dòng)畫代碼
CAKeyframeAnimation *keyframeAnimation=[self translationAnimation];//封裝上面幀動(dòng)畫代碼
animationGroup.animations=@[basicAnimation,keyframeAnimation];

animationGroup.duration=10.0;//設(shè)置動(dòng)畫時(shí)間,如果動(dòng)畫組中動(dòng)畫已經(jīng)設(shè)置過(guò)動(dòng)畫屬性則不再生效
animationGroup.beginTime=CACurrentMediaTime()+5;//延遲五秒執(zhí)行

//3.給圖層添加動(dòng)畫
[_layer addAnimation:animationGroup forKey:nil];
2.4 轉(zhuǎn)場(chǎng)動(dòng)畫CATransition

蘋果封裝的一個(gè)場(chǎng)景以動(dòng)畫形式轉(zhuǎn)到另一個(gè)場(chǎng)景.
步驟:

  1. 創(chuàng)建轉(zhuǎn)場(chǎng)動(dòng)畫
  2. 設(shè)置轉(zhuǎn)場(chǎng)類型,子類型(可選)以及其他屬性;
  3. 設(shè)置轉(zhuǎn)場(chǎng)后的新視圖并添加動(dòng)畫到圖層.

下表列出了常用的轉(zhuǎn)場(chǎng)類型(注意私有API是蘋果官方?jīng)]有公開(kāi)的動(dòng)畫類型,但是目前通過(guò)字符串仍然可以使用):

動(dòng)畫類型 說(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)畫類型還包含子類型:

動(dòng)畫子類型 說(shuō)明
kCATransitionFromRight 從右側(cè)轉(zhuǎn)場(chǎng)
kCATransitionFromLeft 從左側(cè)轉(zhuǎn)場(chǎng)
kCATransitionFromTop 從頂部轉(zhuǎn)場(chǎng)
kCATransitionFromBottom 從底部轉(zhuǎn)場(chǎng)
//1.創(chuàng)建轉(zhuǎn)場(chǎng)動(dòng)畫對(duì)象
CATransition *transition=[[CATransition alloc]init];

//2.設(shè)置動(dòng)畫類型,注意對(duì)于蘋果官方?jīng)]公開(kāi)的動(dòng)畫類型只能使用字符串,并沒(méi)有對(duì)應(yīng)的常量定義
transition.type=@"cube";

    //設(shè)置子類型
if (isNext) {
    transition.subtype=kCATransitionFromRight;
}else{
    transition.subtype=kCATransitionFromLeft;
}
//設(shè)置動(dòng)畫時(shí)常
transition.duration=1.0f;

//3.設(shè)置轉(zhuǎn)場(chǎng)后的新視圖 添加轉(zhuǎn)場(chǎng)動(dòng)畫
_imageView.image=[self getImage:isNext];
[_imageView.layer addAnimation:transition forKey:@"KCTransitionAnimation"];
2.5 UIImageView的序列幀動(dòng)畫.

通過(guò)設(shè)置UIImageView的animationImages屬性,然后調(diào)用它的startAnimating方法去播放這組圖片。(存在著很大的性能問(wèn)題,并且這種方法一旦設(shè)置完圖片中間的過(guò)程就無(wú)法控制了。TOM貓例)

但是對(duì)于一些事物的運(yùn)動(dòng)又不得不選擇使用逐幀動(dòng)畫,例如人得運(yùn)動(dòng),這是一個(gè)高度復(fù)雜的運(yùn)動(dòng),基本動(dòng)畫、關(guān)鍵幀動(dòng)畫是不可能解決的。所大家一定要注意在循環(huán)方法中盡可能的降低算法復(fù)雜度,同時(shí)保證循環(huán)過(guò)程中內(nèi)存峰值盡可能低(延遲釋放)。
可以和其他動(dòng)畫組合使用.


3. UIView動(dòng)畫封裝

UIView本身對(duì)于基本動(dòng)畫和關(guān)鍵幀動(dòng)畫、轉(zhuǎn)場(chǎng)動(dòng)畫都有相應(yīng)的封裝,在對(duì)動(dòng)畫細(xì)節(jié)沒(méi)有特殊要求的情況下使用起來(lái)也要簡(jiǎn)單的多??梢哉f(shuō)在日常開(kāi)發(fā)中90%以上的情況使用UIView的動(dòng)畫封裝方法都可以搞定.

3.1 基礎(chǔ)動(dòng)畫

原理講過(guò)直接上封裝的方法:

 //方法1:block方式
/*開(kāi)始動(dòng)畫,UIView的動(dòng)畫方法執(zhí)行完后動(dòng)畫會(huì)停留在重點(diǎn)位置,而不需要進(jìn)行任何特殊處理
 duration:執(zhí)行時(shí)間
 delay:延遲時(shí)間
 options:動(dòng)畫設(shè)置,例如自動(dòng)恢復(fù)、勻速運(yùn)動(dòng)等
 completion:動(dòng)畫完成回調(diào)方法
 */
//    [UIView animateWithDuration:5.0 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
//        _imageView.center=location;
//    } completion:^(BOOL finished) {
//        NSLog(@"Animation end.");
//    }];



//方法2:靜態(tài)方法
//開(kāi)始動(dòng)畫
[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)畫開(kāi)始運(yùn)行的時(shí)間
//[UIView setAnimationDelegate:self];//設(shè)置代理
//[UIView setAnimationWillStartSelector:(SEL)];//設(shè)置動(dòng)畫開(kāi)始運(yùn)動(dòng)的執(zhí)行方法
//[UIView setAnimationDidStopSelector:(SEL)];//設(shè)置動(dòng)畫運(yùn)行結(jié)束后的執(zhí)行方法

_imageView.center=location;


//提交動(dòng)畫
[UIView commitAnimations];
擴(kuò)展:彈簧動(dòng)畫效果.

由于在iOS開(kāi)發(fā)中彈性動(dòng)畫使用很普遍,所以在iOS7蘋果官方直接提供了一個(gè)方法用于彈性動(dòng)畫開(kāi)發(fā).

/*創(chuàng)建彈性動(dòng)畫  - block
 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];
補(bǔ)充:options-動(dòng)畫參數(shù)

在動(dòng)畫方法中有一個(gè)option參數(shù),UIViewAnimationOptions類型,它是一個(gè)枚舉類型,動(dòng)畫參數(shù)分為三類,可以組合使用:


  • 常規(guī)動(dòng)畫屬性設(shè)置(可以同時(shí)選擇多個(gè)進(jìn)行設(shè)置)

UIViewAnimationOptionLayoutSubviews:動(dòng)畫過(guò)程中保證子視圖跟隨運(yùn)動(dòng)。

UIViewAnimationOptionAllowUserInteraction:動(dòng)畫過(guò)程中允許用戶交互。

UIViewAnimationOptionBeginFromCurrentState:所有視圖從當(dāng)前狀態(tài)開(kāi)始運(yùn)行。

UIViewAnimationOptionRepeat:重復(fù)運(yùn)行動(dòng)畫。

UIViewAnimationOptionAutoreverse :動(dòng)畫運(yùn)行到結(jié)束點(diǎn)后仍然以動(dòng)畫方式回到初始點(diǎn)。

UIViewAnimationOptionOverrideInheritedDuration:忽略嵌套動(dòng)畫時(shí)間設(shè)置。

UIViewAnimationOptionOverrideInheritedCurve:忽略嵌套動(dòng)畫速度設(shè)置。關(guān)鍵幀動(dòng)畫沒(méi)有

UIViewAnimationOptionAllowAnimatedContent:動(dòng)畫過(guò)程中重繪視圖(注意僅僅適用于轉(zhuǎn)場(chǎng)動(dòng)畫)。

UIViewAnimationOptionShowHideTransitionViews:視圖切換時(shí)直接隱藏舊視圖、顯示新視圖,而不是將舊視圖從父視圖移除(僅僅適用于轉(zhuǎn)場(chǎng)動(dòng)畫)
UIViewAnimationOptionOverrideInheritedOptions :不繼承父動(dòng)畫設(shè)置或動(dòng)畫類型。


  • 動(dòng)畫速度控制(可從其中選擇一個(gè)設(shè)置)

UIViewAnimationOptionCurveEaseInOut:動(dòng)畫先緩慢,然后逐漸加速。

UIViewAnimationOptionCurveEaseIn :動(dòng)畫逐漸變慢。

UIViewAnimationOptionCurveEaseOut:動(dòng)畫逐漸加速。

UIViewAnimationOptionCurveLinear :動(dòng)畫勻速執(zhí)行,默認(rèn)值。

  • 轉(zhuǎn)場(chǎng)類型(僅適用于轉(zhuǎn)場(chǎng)動(dòng)畫設(shè)置,可以從中選擇一個(gè)進(jìn)行設(shè)置,基本動(dòng)畫、關(guān)鍵幀動(dòng)畫不需要設(shè)置)

UIViewAnimationOptionTransitionNone:沒(méi)有轉(zhuǎn)場(chǎng)動(dòng)畫效果。

UIViewAnimationOptionTransitionFlipFromLeft :從左側(cè)翻轉(zhuǎn)效果。

UIViewAnimationOptionTransitionFlipFromRight:從右側(cè)翻轉(zhuǎn)效果。

UIViewAnimationOptionTransitionCurlUp:向后翻頁(yè)的動(dòng)畫過(guò)渡效果。

UIViewAnimationOptionTransitionCurlDown :向前翻頁(yè)的動(dòng)畫過(guò)渡效果。

UIViewAnimationOptionTransitionCrossDissolve:舊視圖溶解消失顯示下一個(gè)新視圖的效果。

UIViewAnimationOptionTransitionFlipFromTop :從上方翻轉(zhuǎn)效果。

UIViewAnimationOptionTransitionFlipFromBottom:從底部翻轉(zhuǎn)效果。

3.2 關(guān)鍵幀動(dòng)畫

從iOS7開(kāi)始UIView動(dòng)畫中封裝了關(guān)鍵幀動(dòng)畫

/*關(guān)鍵幀動(dòng)畫
 options:
 */
[UIView animateKeyframesWithDuration:5.0 delay:0 options: UIViewAnimationOptionCurveLinear| UIViewAnimationOptionCurveLinear 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.");
}];
  • 關(guān)鍵幀的options,絕大多數(shù)同上,另有動(dòng)畫模式設(shè)置.

  • 動(dòng)畫模式設(shè)置(同前面關(guān)鍵幀動(dòng)畫動(dòng)畫模式一一對(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)畫有兩種形式,上面演示的是屬性值關(guān)鍵幀動(dòng)畫,路徑關(guān)鍵幀動(dòng)畫目前UIView還不支持。

3.3 轉(zhuǎn)場(chǎng)動(dòng)畫

從iOS4.0開(kāi)始,UIView直接封裝了轉(zhuǎn)場(chǎng)動(dòng)畫.

 [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];
}

上面的轉(zhuǎn)場(chǎng)動(dòng)畫演示中,其實(shí)僅僅有一個(gè)視圖UIImageView做轉(zhuǎn)場(chǎng)動(dòng)畫,每次轉(zhuǎn)場(chǎng)通過(guò)切換UIImageView的內(nèi)容而已。如果有兩個(gè)完全不同的視圖,并且每個(gè)視圖布局都很復(fù)雜,此時(shí)要在這兩個(gè)視圖之間進(jìn)行轉(zhuǎn)場(chǎng)可以使用+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(4_0)方法進(jìn)行兩個(gè)視圖間的轉(zhuǎn)場(chǎng),需要注意的是默認(rèn)情況下轉(zhuǎn)出的視圖會(huì)從父視圖移除,轉(zhuǎn)入后重新添加,可以通過(guò) UIViewAnimationOptionShowHideTransitionViews參數(shù)設(shè)置,設(shè)置此參數(shù)后轉(zhuǎn)出的視圖會(huì)隱藏(不會(huì)移除)轉(zhuǎn)入后再顯示。

注意:轉(zhuǎn)場(chǎng)動(dòng)畫設(shè)置參數(shù)完全同基本動(dòng)畫參數(shù)設(shè)置;同直接使用轉(zhuǎn)場(chǎng)動(dòng)畫不同的是使用UIView的裝飾方法進(jìn)行轉(zhuǎn)場(chǎng)動(dòng)畫其動(dòng)畫效果較少,因?yàn)檫@里無(wú)法直接使用私有API。

參考博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,690評(píng)論 6 30
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你可以看...
    F麥子閱讀 5,268評(píng)論 5 13
  • 概覽 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你...
    Yiart閱讀 3,965評(píng)論 3 34
  • 在iOS實(shí)際開(kāi)發(fā)中常用的動(dòng)畫無(wú)非是以下四種:UIView動(dòng)畫,核心動(dòng)畫,幀動(dòng)畫,自定義轉(zhuǎn)場(chǎng)動(dòng)畫。 1.UIView...
    請(qǐng)叫我周小帥閱讀 3,322評(píng)論 1 23
  • 概覽 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你...
    被吹落的風(fēng)閱讀 1,705評(píng)論 1 2

友情鏈接更多精彩內(nèi)容