以前由於項目需要 也寫了一些動畫 ,但是知識不系統,很散。這段時間趁着項目完成的空襲,來跟着大神的腳步系統的總結一下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; }