傳送門:《iOS核心動(dòng)畫高級(jí)技巧》部分源碼
CA利用RunLoop收集圖層 Animatable屬性 的修改(如
backgroundColor
等)偿衰,并基于事務(wù)對(duì)【隱式動(dòng)畫】進(jìn)行管理;簡單來說隱式動(dòng)畫只能完成部分基礎(chǔ)屬性動(dòng)畫,而【顯式動(dòng)畫】則通過 關(guān)鍵幀動(dòng)畫澜术、動(dòng)畫組 對(duì)屬性動(dòng)畫提供更具體的控制或組合庭再,通過 過渡 支持 NonAnimatable屬性和圖層樹的變化膳汪;而【圖層時(shí)間】介紹了Core Animation用來操作時(shí)間控制動(dòng)畫的機(jī)制:CAMediaTiming協(xié)議和層級(jí)關(guān)系時(shí)間茄袖,并給出了手動(dòng)動(dòng)畫的通用解決方案;【緩沖】通過控制 速度 使動(dòng)畫更平滑更自然雏胃;如果需要實(shí)時(shí)控制動(dòng)畫/更強(qiáng)的交互控制動(dòng)畫请毛,可以使用【基于定時(shí)器的動(dòng)畫】。
從結(jié)構(gòu)上來說瞭亮,CAAnimation包括計(jì)時(shí)函數(shù)(CAMediaTimingFunction)方仿、一個(gè)圖層委托(CALayerDelegate,用于反饋動(dòng)畫狀態(tài))和一個(gè)removedOnCompletion(標(biāo)識(shí)動(dòng)畫是否該在結(jié)束后自動(dòng)釋放,默認(rèn)YES ,為了防止內(nèi)存泄露)仙蚜,另外還實(shí)現(xiàn)了以下協(xié)議:
- CAAction (允許 CAAnimation 的子類提供圖層行為)
- CAMediaTiming (第九章“圖層時(shí)間”將會(huì)詳細(xì)解釋)此洲。
我發(fā)現(xiàn)一個(gè)很奇怪的問題:使用animation.beginTime有時(shí)導(dǎo)致動(dòng)畫失效、有時(shí)類似無序的timeOffset鳍征,未能實(shí)現(xiàn)延遲開始動(dòng)畫的功能黍翎?面徽?艳丛?
一些常用的緩沖函數(shù):
RBBAnimation
AHEasing
第七章:隱式動(dòng)畫
隱式動(dòng)畫:Core Animation在每個(gè) RunLoop 周期中自動(dòng)開始一次新的事務(wù)(RunLoop是iOS負(fù)責(zé)收集用戶輸入、處理定時(shí)器或者網(wǎng)絡(luò)事件并且重新繪制屏幕的東西)趟紊,即使不顯式調(diào)用
[CATransaction begin];
開始一次事務(wù)氮双,任何在一次RunLoop循環(huán)中Animatable屬性的改變也會(huì)被集中起來,然后Core Animation根據(jù)圖層行為和事務(wù)設(shè)置(默認(rèn)0.25秒)去不斷更新視圖的這些屬性在屏幕上的狀態(tài)霎匈。
- 呈現(xiàn)和模型
-
呈現(xiàn)樹 - 圖層的當(dāng)前位置
- -presentationLayer
- 呈現(xiàn)圖層實(shí)際上是模型圖層的復(fù)制戴差,但是它的屬性值代表了在任何指定時(shí)刻當(dāng)前外觀效果。換句話說铛嘱,你可以通過呈現(xiàn)圖層的值來獲取當(dāng)前屏幕上真正顯示出來的值
- 呈現(xiàn)圖層僅僅當(dāng)圖層首次被提交(就是第一次在屏幕上顯示)的時(shí)候創(chuàng)建暖释,所以在那之前調(diào)用-presentationLayer將會(huì)返回nil。
- 需要使用呈現(xiàn)圖層的兩種情況:
- 同步動(dòng)畫 - 如果你在實(shí)現(xiàn)一個(gè)基于定時(shí)器的動(dòng)畫(見第11章“基于定時(shí)器的動(dòng)畫”)墨吓,而不僅僅是基于事務(wù)的動(dòng)畫球匕,這個(gè)時(shí)候準(zhǔn)確地知道在某一時(shí)刻圖層顯示在什么位置就會(huì)對(duì)正確擺放圖層很有用了。
處理用戶交互 - 如果你想讓你做動(dòng)畫的圖層響應(yīng)用戶輸入帖烘,你可以使用-hitTest:方法來判斷指定圖層是否被觸摸亮曹,這時(shí)候?qū)?em>呈現(xiàn)圖層而不是模型圖層調(diào)用-hitTest:會(huì)顯得更有意義,因?yàn)槌尸F(xiàn)圖層代表了用戶當(dāng)前看到的圖層位置秘症,而不是當(dāng)前動(dòng)畫結(jié)束之后的位置照卦。
- 模型樹 - 圖層將要到達(dá)的位置
- –modelLayer
- 當(dāng)設(shè)置CALayer的屬性,實(shí)際上是在定義當(dāng)前事務(wù)結(jié)束之后圖層如何顯示的模型乡摹。
-
呈現(xiàn)樹 - 圖層的當(dāng)前位置
第八章:顯式動(dòng)畫
- 8.1.1 CAPropertyAnimation - 屬性動(dòng)畫
- 1-CABasicAnimation
- 特別注意:需要同步更新目標(biāo)屬性的值
- 在動(dòng)畫開始之前更新
- 特別注意:需要同步更新目標(biāo)屬性的值
- 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];
}
- 在動(dòng)畫之后更新(需結(jié)合KVC和fillMode) - CAAnimationDelegate
-(void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag
{
// CAAnimation實(shí)現(xiàn)了KVC(鍵-值-編碼)協(xié)議役耕,于是你可以用-setValue:forKey:和-valueForKey:方法來存取屬性。
// 可以利用它來判斷到底是哪個(gè)圖層的調(diào)用
// 另外聪廉,為了確保更新屬性更新發(fā)生在動(dòng)畫返回初始狀態(tài)之前蹄葱,還得考慮fillMode!3小图云!
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
[CATransaction commit];
}
- 2-CAKeyframeAnimation
- 虛擬屬性 - transform.rotation
- Core Animation自動(dòng)通過CAValueFunction計(jì)算的值來更新屬性
- 我們可以不通過關(guān)鍵幀一步旋轉(zhuǎn)多于180度的動(dòng)畫。
- 可以用相對(duì)值而不是絕對(duì)值旋轉(zhuǎn)(設(shè)置byValue而不是toValue)邻邮。
- 可以不用創(chuàng)建CATransform3D竣况,而是使用一個(gè)簡單的數(shù)值來指定角度。
- 不會(huì)和transform.position或者transform.scale沖突(同樣是使用關(guān)鍵路徑來做獨(dú)立的動(dòng)畫屬性)筒严。
- 虛擬屬性 - transform.rotation
- 8.1.2 CAAnimationGroup - 動(dòng)畫組
- 8.1.3 CATransition - 過渡
- 影響整個(gè)圖層丹泉,可以用來對(duì)圖層的任何內(nèi)容做任何類型的動(dòng)畫情萤,包括子圖層的添加和移除。
kCATransitionFade //平滑的淡入淡出效果
kCATransitionPush //從頂部滑動(dòng)進(jìn)入摹恨,但不像推送動(dòng)畫那樣把老圖層推走
kCATransitionMoveIn //新圖層從邊緣的一側(cè)滑動(dòng)進(jìn)來筋岛,把舊圖層從另一側(cè)推出去的效果
kCATransitionReveal //把原始的圖層滑動(dòng)出去來顯示新的外觀,而不是把新的圖層滑動(dòng)進(jìn)入
+ [subtype](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CATransition_class/index.html#//apple_ref/occ/instp/CATransition/subtype) - kCATransitionFromRight/Top/Left/Bottom
+ 對(duì)指定的圖層一次只能使用一次CATransition
[self.imageView.layer addAnimation:transition forKey:nil]; //使用默認(rèn)鍵 - kCATransition
-
隱式過渡
- 對(duì)于你自己創(chuàng)建的圖層晒哄,這意味著對(duì)圖層contents圖片做的改動(dòng)都會(huì)自動(dòng)附上淡入淡出的動(dòng)畫睁宰。
- 對(duì)于視圖關(guān)聯(lián)的圖層,或者是其他隱式動(dòng)畫的行為寝凌,這個(gè)特性依然是被禁用的
-
自定義動(dòng)畫
- UIView的兩個(gè)方法提供了Core Animation的過渡特性
+transitionFromView:toView:duration:options:completion://可能有圖層樹變化
+transitionWithView:duration:options:animations://圖層樹不變
- CALayer有一個(gè)-renderInContext:方法柒傻,可以通過把它繪制到Core Graphics的上下文中捕獲當(dāng)前內(nèi)容的圖片,然后把這個(gè)截屏視圖置于原始視圖之上较木,遮住真實(shí)視圖的所有變化红符,于是創(chuàng)建了一個(gè)簡單的過渡效果
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
- -renderInContext:捕獲了圖層的圖片和子圖層,但是不能對(duì)子圖層正確地處理變換效果伐债,而且對(duì)視頻和OpenGL內(nèi)容也不起作用预侯。但是用CATransition,或者用私有的截屏方式就沒有這個(gè)限制了峰锁?萎馅??
8.2 在動(dòng)畫過程中取消動(dòng)畫
- 注意:動(dòng)畫一旦被移除祖今,圖層的外觀就立刻更新到當(dāng)前的模型圖層的值
第九章:圖層時(shí)間
Core Animation是如何跟蹤時(shí)間的
- CAMediaTiming協(xié)議
- CALayer和CAAnimation都實(shí)現(xiàn)了這個(gè)協(xié)議校坑,所以動(dòng)畫時(shí)間可以被任意基于一個(gè)圖層或者一段動(dòng)畫的類控制。
- 層級(jí)關(guān)系時(shí)間
- 全局時(shí)間和本地時(shí)間
- 全局時(shí)間 - 設(shè)備休眠時(shí)會(huì)暫停
CFTimeInterval time = CACurrentMediaTime(); // 馬赫時(shí)間:對(duì)動(dòng)畫的時(shí)間測量提供了一個(gè)相對(duì)值
- 本地時(shí)間
每個(gè)CALayer和CAAnimation實(shí)例都有自己本地時(shí)間的概念千诬,是根據(jù)父圖層/動(dòng)畫層級(jí)關(guān)系中的beginTime
,timeOffset
和speed
屬性計(jì)算耍目。下述方法用來同步不同圖層之間有不同的speed
,timeOffset
和beginTime
的動(dòng)畫:
-(CFTimeInterval)convertTime:(CFTimeInterval)t from/toLayer:(CALayer *)l;
第十章:緩沖
使動(dòng)畫移動(dòng)更平滑更自然
第十一章:基于定時(shí)器的動(dòng)畫
允許我們精確地控制一幀一幀展示;iOS按照每秒60次刷新屏幕徐绑,CAAnimation最機(jī)智的地方在于每次刷新需要展示的時(shí)候去計(jì)算插值和緩沖邪驮。
- 定時(shí)幀
-
NSTimer
受RunLoop中的任務(wù)列表影響,可能會(huì)有很大的延遲
優(yōu)化:- 我們可以用CADisplayLink讓更新頻率嚴(yán)格控制在每次屏幕刷新之后傲茄。
- 基于真實(shí)幀的持續(xù)時(shí)間而不是假設(shè)的更新頻率來做動(dòng)畫毅访。
- 調(diào)整動(dòng)畫計(jì)時(shí)器的run loop模式,這樣就不會(huì)被別的事件干擾盘榨。
-
CADisplayLink
- 不能保證每一幀都按計(jì)劃執(zhí)行喻粹,一些失去控制的離散的任務(wù)或者事件(例如資源緊張的后臺(tái)程序)可能會(huì)導(dǎo)致動(dòng)畫偶爾地丟幀。當(dāng)使用NSTimer的時(shí)候草巡,一旦有機(jī)會(huì)計(jì)時(shí)器就會(huì)開啟守呜,但是CADisplayLink卻不一樣:如果它丟失了幀,就會(huì)直接忽略它們,然后在下一次更新的時(shí)候接著運(yùn)行查乒。
-
計(jì)算幀的持續(xù)時(shí)間
然后根據(jù)緩沖函數(shù)計(jì)算下一幀的目標(biāo)位置弥喉,達(dá)到精確控制、動(dòng)畫平滑
-
物理模擬-
Chipmunk- 基礎(chǔ)類
- cpSpace - 所有的物理結(jié)構(gòu)體的容器 (一個(gè)大小和一個(gè)可選的重力矢量)
- cpBody - 固態(tài)無彈力的剛體 (坐標(biāo)+其他物理屬性玛迄,例如質(zhì)量由境,運(yùn)動(dòng)和摩擦系數(shù)等等)
- cpShape - 抽象的幾何形狀,有各種子類代表不同形狀 (比如cpPolyShape)蓖议,可以給結(jié)構(gòu)體添加一個(gè)多邊形用來檢測碰撞虏杰。
- 添加用戶交互
- cpShapeSetCollisionType
- cpSpaceAddStaticShape
- 模擬時(shí)間以及固定的時(shí)間步長
根據(jù)屏幕刷新的時(shí)間跟蹤時(shí)間步長,然后根據(jù)每幀去計(jì)算一個(gè)或者多個(gè)模擬出來的效果拒担。- 對(duì)于實(shí)現(xiàn)動(dòng)畫的緩沖效果來說嘹屯,計(jì)算每幀持續(xù)的時(shí)間是一個(gè)很好的解決方案
- 對(duì)模擬物理效果則應(yīng)該使用固定的時(shí)間步長
-
while (self.lastStep < frameTime) {
cpSpaceStep(self.space, SIMULATION_STEP);
self.lastStep += SIMULATION_STEP;
}
- 避免死亡螺旋
+ cpSpaceStep()的計(jì)算造成幀率延遲的塔羅牌堆積效應(yīng)