CA利用RunLoop收集圖層 Animatable屬性 的修改(如
backgroundColor
等),并基于事務對【隱式動畫】進行管理;簡單來說隱式動畫只能完成部分基礎屬性動畫,而【顯式動畫】則通過 關鍵幀動畫、動畫組 對屬性動畫提供更具體的控制或組合,通過 過渡 支持 NonAnimatable屬性和圖層樹的變化;而【圖層時間】介紹了Core Animation用來操作時間控制動畫的機制:CAMediaTiming協議和層級關系時間,并給出了手動動畫的通用解決方案;【緩沖】通過控制 速度 使動畫更平滑更自然;如果需要實時控制動畫/更強的交互控制動畫,可以使用【基于定時器的動畫】。
從結構上來說,CAAnimation包括計時函數(CAMediaTimingFunction)、一個圖層委托(CALayerDelegate,用于反饋動畫狀態)和一個removedOnCompletion(標識動畫是否該在結束后自動釋放,默認YES ,為了防止內存泄露),另外還實現了以下協議:
- CAAction (允許 CAAnimation 的子類提供圖層行為)
- CAMediaTiming (第九章“圖層時間”將會詳細解釋)。
我發現一個很奇怪的問題:使用animation.beginTime有時導致動畫失效、有時類似無序的timeOffset,未能實現延遲開始動畫的功能???
一些常用的緩沖函數:
RBBAnimation
AHEasing
第七章:隱式動畫
隱式動畫:Core Animation在每個 RunLoop 周期中自動開始一次新的事務(RunLoop是iOS負責收集用戶輸入、處理定時器或者網絡事件并且重新繪制屏幕的東西),即使不顯式調用
[CATransaction begin];
開始一次事務,任何在一次RunLoop循環中Animatable屬性的改變也會被集中起來,然后Core Animation根據圖層行為和事務設置(默認0.25秒)去不斷更新視圖的這些屬性在屏幕上的狀態。
- 呈現和模型
-
呈現樹 - 圖層的當前位置
- -presentationLayer
- 呈現圖層實際上是模型圖層的復制,但是它的屬性值代表了在任何指定時刻當前外觀效果。換句話說,你可以通過呈現圖層的值來獲取當前屏幕上真正顯示出來的值
- 呈現圖層僅僅當圖層首次被提交(就是第一次在屏幕上顯示)的時候創建,所以在那之前調用-presentationLayer將會返回nil。
- 需要使用呈現圖層的兩種情況:
- 同步動畫 - 如果你在實現一個基于定時器的動畫(見第11章“基于定時器的動畫”),而不僅僅是基于事務的動畫,這個時候準確地知道在某一時刻圖層顯示在什么位置就會對正確擺放圖層很有用了。
處理用戶交互 - 如果你想讓你做動畫的圖層響應用戶輸入,你可以使用-hitTest:方法來判斷指定圖層是否被觸摸,這時候對呈現圖層而不是模型圖層調用-hitTest:會顯得更有意義,因為呈現圖層代表了用戶當前看到的圖層位置,而不是當前動畫結束之后的位置。
- 模型樹 - 圖層將要到達的位置
- –modelLayer
- 當設置CALayer的屬性,實際上是在定義當前事務結束之后圖層如何顯示的模型。
-
呈現樹 - 圖層的當前位置
第八章:顯式動畫
8.1 CAAnimation體系結構
- 8.1.1 CAPropertyAnimation - 屬性動畫
- 1-CABasicAnimation
- 特別注意:需要同步更新目標屬性的值
- 在動畫開始之前更新
- 特別注意:需要同步更新目標屬性的值
- 1-CABasicAnimation
-(void)applyBasicAnimation:(CABasicAnimation *)animation toLayer:(CALayer *)layer{
animation.fromValue = [layer.presentationLayer ?: layer valueForKeyPath:animation.keyPath];
// note: this approach will only work if toValue != nil
[CATransaction begin];
[CATransaction setDisableActions:YES];
[layer setValue:animation.toValue forKeyPath:animation.keyPath];
[CATransaction commit];
[layer addAnimation:animation forKey:nil];
}
- 在動畫之后更新(需結合KVC和fillMode) - CAAnimationDelegate
-(void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag
{
// CAAnimation實現了KVC(鍵-值-編碼)協議,于是你可以用-setValue:forKey:和-valueForKey:方法來存取屬性。
// 可以利用它來判斷到底是哪個圖層的調用
// 另外,為了確保更新屬性更新發生在動畫返回初始狀態之前,還得考慮fillMode?。?!
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
[CATransaction commit];
}
- 2-CAKeyframeAnimation
- 虛擬屬性 - transform.rotation
- Core Animation自動通過CAValueFunction計算的值來更新屬性
- 我們可以不通過關鍵幀一步旋轉多于180度的動畫。
- 可以用相對值而不是絕對值旋轉(設置byValue而不是toValue)。
- 可以不用創建CATransform3D,而是使用一個簡單的數值來指定角度。
- 不會和transform.position或者transform.scale沖突(同樣是使用關鍵路徑來做獨立的動畫屬性)。
- 虛擬屬性 - transform.rotation
- 8.1.2 CAAnimationGroup - 動畫組
- 8.1.3 CATransition - 過渡
- 影響整個圖層,可以用來對圖層的任何內容做任何類型的動畫,包括子圖層的添加和移除。
kCATransitionFade //平滑的淡入淡出效果
kCATransitionPush //從頂部滑動進入,但不像推送動畫那樣把老圖層推走
kCATransitionMoveIn //新圖層從邊緣的一側滑動進來,把舊圖層從另一側推出去的效果
kCATransitionReveal //把原始的圖層滑動出去來顯示新的外觀,而不是把新的圖層滑動進入
+ [subtype](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CATransition_class/index.html#//apple_ref/occ/instp/CATransition/subtype) - kCATransitionFromRight/Top/Left/Bottom
+ 對指定的圖層一次只能使用一次CATransition
[self.imageView.layer addAnimation:transition forKey:nil]; //使用默認鍵 - kCATransition
-
隱式過渡
- 對于你自己創建的圖層,這意味著對圖層contents圖片做的改動都會自動附上淡入淡出的動畫。
- 對于視圖關聯的圖層,或者是其他隱式動畫的行為,這個特性依然是被禁用的
-
自定義動畫
- UIView的兩個方法提供了Core Animation的過渡特性
+transitionFromView:toView:duration:options:completion://可能有圖層樹變化
+transitionWithView:duration:options:animations://圖層樹不變
- CALayer有一個-renderInContext:方法,可以通過把它繪制到Core Graphics的上下文中捕獲當前內容的圖片,然后把這個截屏視圖置于原始視圖之上,遮住真實視圖的所有變化,于是創建了一個簡單的過渡效果
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
- -renderInContext:捕獲了圖層的圖片和子圖層,但是不能對子圖層正確地處理變換效果,而且對視頻和OpenGL內容也不起作用。但是用CATransition,或者用私有的截屏方式就沒有這個限制了???
8.2 在動畫過程中取消動畫
- 注意:動畫一旦被移除,圖層的外觀就立刻更新到當前的模型圖層的值
第九章:圖層時間
Core Animation是如何跟蹤時間的
- CAMediaTiming協議
- CALayer和CAAnimation都實現了這個協議,所以動畫時間可以被任意基于一個圖層或者一段動畫的類控制。
- 層級關系時間
- 全局時間和本地時間
- 全局時間 - 設備休眠時會暫停
CFTimeInterval time = CACurrentMediaTime(); // 馬赫時間:對動畫的時間測量提供了一個相對值
- 本地時間
每個CALayer和CAAnimation實例都有自己本地時間的概念,是根據父圖層/動畫層級關系中的beginTime
,timeOffset
和speed
屬性計算。下述方法用來同步不同圖層之間有不同的speed
,timeOffset
和beginTime
的動畫:
-(CFTimeInterval)convertTime:(CFTimeInterval)t from/toLayer:(CALayer *)l;
第十章:緩沖
使動畫移動更平滑更自然
第十一章:基于定時器的動畫
允許我們精確地控制一幀一幀展示;iOS按照每秒60次刷新屏幕,CAAnimation最機智的地方在于每次刷新需要展示的時候去計算插值和緩沖。
- 定時幀
-
NSTimer
受RunLoop中的任務列表影響,可能會有很大的延遲
優化:- 我們可以用CADisplayLink讓更新頻率嚴格控制在每次屏幕刷新之后。
- 基于真實幀的持續時間而不是假設的更新頻率來做動畫。
- 調整動畫計時器的run loop模式,這樣就不會被別的事件干擾。
-
CADisplayLink
- 不能保證每一幀都按計劃執行,一些失去控制的離散的任務或者事件(例如資源緊張的后臺程序)可能會導致動畫偶爾地丟幀。當使用NSTimer的時候,一旦有機會計時器就會開啟,但是CADisplayLink卻不一樣:如果它丟失了幀,就會直接忽略它們,然后在下一次更新的時候接著運行。
-
計算幀的持續時間
然后根據緩沖函數計算下一幀的目標位置,達到精確控制、動畫平滑
-
物理模擬-
Chipmunk- 基礎類
- cpSpace - 所有的物理結構體的容器 (一個大小和一個可選的重力矢量)
- cpBody - 固態無彈力的剛體 (坐標+其他物理屬性,例如質量,運動和摩擦系數等等)
- cpShape - 抽象的幾何形狀,有各種子類代表不同形狀 (比如cpPolyShape),可以給結構體添加一個多邊形用來檢測碰撞。
- 添加用戶交互
- cpShapeSetCollisionType
- cpSpaceAddStaticShape
- 模擬時間以及固定的時間步長
根據屏幕刷新的時間跟蹤時間步長,然后根據每幀去計算一個或者多個模擬出來的效果。- 對于實現動畫的緩沖效果來說,計算每幀持續的時間是一個很好的解決方案
- 對模擬物理效果則應該使用固定的時間步長
-
while (self.lastStep < frameTime) {
cpSpaceStep(self.space, SIMULATION_STEP);
self.lastStep += SIMULATION_STEP;
}
- 避免死亡螺旋
+ cpSpaceStep()的計算造成幀率延遲的塔羅牌堆積效應