iOS CoreAnimation詳解(一) 有關Layer的動畫


以前由於項目需要 也寫了一些動畫 ,但是知識不系統,很散。這段時間趁着項目完成的空襲,來跟着大神的腳步系統的總結一下iOS中Core Animation的知識點。

原博客地址:http://blog.csdn.net/column/details/huangwenchen-ios-sdk.html

本文主要從CoreAnimation的Layer角度來講解動畫,我想從CALayer的角度更好理解,后續還會有第二篇從UIKIt的UIView角度來講解動畫,第三篇講解UIDynamicAnimation,第三篇我會講到UIViewController切換時候的動畫。

本文主要涵蓋四個部分

1.基礎動畫 會講到時間函數和一些關鍵的屬性

2.基於關鍵幀的動畫 講到沿着指定路徑運行的動畫

3.動畫組 多個動畫組合到一起形成復雜的動畫

4.簡單講一講有關動畫的代理

一 為什么要設計動畫

動畫提供了一個漸變的方式來表達變化,使用動畫可以避免各種動畫突變,造成用戶困惑。

iOS中,使用CoreAnimation只要指定始末狀態或者關鍵幀狀態,CoreAnimation會高效的為我們創建補間動畫。

二 從CALayer的角度來看三種動畫

首先不熟悉CALayer的同學看看前兩篇的CALayer的內容,這是CoreAnimation的基礎。這里我重復的介紹兩種CALayer的Tree。
Presentation Tree-對應在動畫的過程中,CALayer的屬性
Model Tree-對應CALayer的實際屬性。
通過使用 -[CALayer presentationLayer] 和 -[CALayer modelLayer]可以訪問兩種Tree
動畫的過程實際上是修改Presentation Tree

2.1 基礎的動畫 CABasicAnimation

屬性說明

動畫過程說明

隨着動畫的進行,在長度為duration的持續時間內,keyPath相應屬性的值從fromValue漸漸地變為toValue。

keyPath內容是CALayer的可動畫Animatable屬性。

如果fillMode = kCAFillModeForwards同時removedOnComletion = NO,那么在動畫執行完畢后,圖層會保持顯示動畫執行后的狀態。但在實質上,圖層的屬性值還是動畫執行前的初始值,並沒有真正被改變。

CAKeyframeAnimation——關鍵幀動畫

關鍵幀動畫,也是CAPropertyAnimation的子類,與CABasicAnimation的區別是:

CABasicAnimation只能從一個數值(fromValue)變到另一個數值(toValue),而CAKeyframeAnimation會使用一個NSArray保存這些數值
CABasicAnimation可看做是只有2個關鍵幀的CAKeyframeAnimation

屬性說明:

 

CAAnimationGroup——動畫組

動畫組,是CAAnimation的子類,可以保存一組動畫對象,將CAAnimationGroup對象加入層后,組中所有動畫對象可以同時並發運行。

默認情況下,一組動畫對象是同時運行的,也可以通過設置動畫對象的beginTime屬性來更改動畫的開始時間。

屬性說明:

第一個簡單的動畫,我希望imageview向右移動100的距離,移動方式easeInOut(加速開始,減速結束)。

代碼如下,通常有兩種方式來影響動畫

CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"position.x";//KVC的方式來訪問屬性
    animation.fromValue = @(self.imageView.layer.position.x);//該屬性開始的值
    animation.toValue = @(self.imageView.layer.position.x + 100);//該屬性的結束值
    animation.duration = 1;//完成一次動畫需要的時間
    //設置動畫進行的方式
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    [self.imageView.layer addAnimation:animation forKey:@"basic"];

通過fromValue和toValue是一種方式,當然也可以通過byValue的方式,byValue在初值上加上byValue的變化,通過以下代碼也可以實現上述動畫

    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"position.x";//KVC的方式來訪問屬性
//    animation.fromValue = @(self.imageView.layer.position.x);//該屬性開始的值
//    animation.toValue = @(self.imageView.layer.position.x + 100);//該屬性的結束值
    animation.byValue = @(100);
    animation.duration = 1;//完成一次動畫需要的時間
    //設置動畫進行的方式
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    [self.imageView.layer addAnimation:animation forKey:@"basic"];

但是,結束后會發現,imageview又恢復到原處。這是因為在動畫的過程中,我們修改的是Presentation Tree,並沒有實際修改CALayer的屬性。想要讓動畫停在結束的位置,通常有兩種方式,

(1)修改屬性
代碼如下

CABasicAnimation * animation = [CABasicAnimation animation];  
  animation.keyPath = @"position.x";  
  animation.fromValue = @(self.imageview.layer.position.x);  
  animation.toValue = @(self.imageview.layer.position.x + 100);  
  animation.duration = 1;  
  animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];  
  [self.imageview.layer addAnimation:animation forKey:@"basic"];  
  self.imageview.layer.position = CGPointMake(self.imageview.layer.position.x+100, self.imageview.layer.position.y);  

(2)設置讓動畫停在結束的位置

CABasicAnimation * animation = [CABasicAnimation animation];  
    animation.keyPath = @"position.x";  
    animation.fromValue = @(self.imageview.layer.position.x);  
    animation.toValue = @(self.imageview.layer.position.x + 100);  
    animation.duration = 1;  
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];  
    animation.removedOnCompletion = NO;//動畫結束了禁止刪除  
    animation.fillMode = kCAFillModeForwards;//停在動畫結束處  
    [self.imageview.layer addAnimation:animation forKey:@"basic"];  

一般采用前者,因為動畫往往的結束是實際的屬性的改變。

這里再講解下時間函數
時間函數決定了動畫如何執行,時間函數決定了動畫的數學模型,比如速度速度最好不要有突變, 系統提供的時間函數有以下幾種

NSString *constkCAMediaTimingFunctionLinear;線性
NSString *constkCAMediaTimingFunctionEaseIn;加速進入
NSString *constkCAMediaTimingFunctionEaseOut;減速停止
NSString*constkCAMediaTimingFunctionEaseInEaseOut;加速進入減速停止,這個是常用的
NSString *constkCAMediaTimingFunctionDefault;默認
當然,時間函數支持自定義,用如下函數
functionWithControlPoints::::
這個函數的4個點決定了一個三維的貝塞爾曲線來決定時間函數。這里不深入講解了。
最后,這一點尤為重要,就是在傳遞CAAnimation的對象或者子類給Layer的時候,傳遞的是copy

2.2 基於關鍵幀的動畫
以下是一個基於關鍵幀創建的抖動的動畫,采用在時間點對應的位置來創建動畫

CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position.x";
    NSInteger initalPositionX = self.imageView.layer.position.x;
    animation.values = @[@(initalPositionX),
                         @(initalPositionX + 10),
                         @(initalPositionX - 10),
                         @(initalPositionX + 10),
                         @(initalPositionX)];
    animation.keyTimes = @[
                           @(0),
                           @(1/6.0),
                           @(3/6.0),
                           @(5/6.0),
                           @(1)];
    [self.imageView.layer addAnimation:animation forKey:@"keyFrame"];

當然,基於關鍵幀的動畫支持沿着路徑運動,可以設置時間函數來決定運動的方式
例如以下創建一個比較復雜的運動,首先移動到(200,200),然后沿着以該點為圓心,逆時針旋轉半圈,最后停在結束的位置。
動畫的過程中,imageview沿着路徑轉動

CAKeyframeAnimation * animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    //Create Path
    CGMutablePathRef mutablepath = CGPathCreateMutable();
    CGPathMoveToPoint(mutablepath, nil,self.imageView.layer.position.x, self.imageView.layer.position.y);
    CGPathAddLineToPoint(mutablepath,nil,200,200);
    CGPathAddArc(mutablepath, nil,200,200,100,0,M_PI,YES);
    //set path
    animation.path = mutablepath;
    animation.duration = 4.0;
    animation.rotationMode = kCAAnimationRotateAuto;
    animation.removedOnCompletion = NO;//動畫結束了禁止刪除
    animation.fillMode = kCAFillModeForwards;//停在動畫結束處
    [self.imageView.layer addAnimation:animation forKey:@"PathAnimation"];

2.3 動畫組
所謂,動畫組就是把幾個動畫組合到一起,然后一起執行,通常復雜的動畫都是由動畫組來實現的。
舉個例子:沿着上個例子的路徑運動,運動的同時透明度漸變。

CAKeyframeAnimation * pathAnimation = [CAKeyframeAnimation animation];  
  pathAnimation.keyPath = @"position";  
  //Create Path  
  CGMutablePathRef mutablepath = CGPathCreateMutable();  
  CGPathMoveToPoint(mutablepath, nil,self.imageview.layer.position.x, self.imageview.layer.position.y);  
  CGPathAddLineToPoint(mutablepath,nil,200,200);  
  CGPathAddArc(mutablepath, nil,200,200,100,0,M_PI,YES);  
  //set path  
  pathAnimation.path = mutablepath;  
  pathAnimation.rotationMode = kCAAnimationRotateAuto;  
  [self.imageview.layer addAnimation:pathAnimation forKey:@"PathAnimation"];  
    
  //透明度變化  
  CAKeyframeAnimation * opacityAnimation = [CAKeyframeAnimation animation];  
  opacityAnimation.keyPath = @"opacity";  
  opacityAnimation.values = @[@(1.0),  
                       @(0.5),  
                       @(0.0),  
                       @(0.5),  
                       @(1.0)];  
  opacityAnimation.calculationMode = kCAAnimationPaced;  
  [self.imageview.layer addAnimation:opacityAnimation forKey:@"OpacityAnination"];  
  //配置動畫組  
  CAAnimationGroup * animationGroup = [[CAAnimationGroup alloc] init];  
  animationGroup.animations = @[pathAnimation,opacityAnimation];  
  animationGroup.duration = 4.0;  
  animationGroup.removedOnCompletion = NO;  
  animationGroup.fillMode = kCAFillModeBackwards;  
  [self.imageview.layer addAnimation:animationGroup forKey:@"GroupAnimation"];  

(三)動畫的代理
通過設置代理可以監聽動畫開始和結束的事件
通過設置delegate,來監聽兩個函數
animationDidStart:(CAAnimation *)anim
animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
這里的flag判斷動畫是否執行完畢

4.付coreAnimation中的keyPath 屬性

CAAnimation——簡介

是所有動畫對象的父類,負責控制動畫的持續時間和速度,是個抽象類,不能直接使用,應該使用它具體的子類。

基本屬性說明

fillMode 屬性設置

kCAFillModeRemoved 這個是默認值,也就是說當動畫開始前和動畫結束后,動畫對layer都沒有影響,動畫結束后,layer會恢復到之前的狀態

kCAFillModeForwards 當動畫結束后,layer會一直保持着動畫最后的狀態

kCAFillModeBackwards 在動畫開始前,只需要將動畫加入了一個layer,layer便立即進入動畫的初始狀態並等待動畫開始。

kCAFillModeBoth 這個其實就是上面兩個的合成.動畫加入后開始之前,layer便處於動畫初始狀態,動畫結束后layer保持動畫最后的狀態

CALayer上動畫的暫停和恢復

#pragma mark 暫停CALayer的動畫
-(void)pauseLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];

    // 讓CALayer的時間停止走動
      layer.speed = 0.0;
    // 讓CALayer的時間停留在pausedTime這個時刻
    layer.timeOffset = pausedTime;
}

#pragma mark 恢復CALayer的動畫
-(void)resumeLayer:(CALayer*)layer
{
    CFTimeInterval pausedTime = layer.timeOffset;
    // 1. 讓CALayer的時間繼續行走
      layer.speed = 1.0;
    // 2. 取消上次記錄的停留時刻
      layer.timeOffset = 0.0;
    // 3. 取消上次設置的時間
      layer.beginTime = 0.0;
    // 4. 計算暫停的時間(這里也可以用CACurrentMediaTime()-pausedTime)
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    // 5. 設置相對於父坐標系的開始時間(往后退timeSincePause)
      layer.beginTime = timeSincePause;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM