鍍金池/ 教程/ iOS/ 動畫解釋
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計 App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計優(yōu)雅的移動游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動畫
常見的后臺實(shí)踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

動畫解釋

我們寫的應(yīng)用程序往往都不是靜態(tài)的,因?yàn)樗鼈冃枰m應(yīng)用戶的需求以及為執(zhí)行各種任務(wù)而改變狀態(tài)。

在這些狀態(tài)之間轉(zhuǎn)換時,清晰的揭示正在發(fā)生什么是非常重要的。而不是在頁面之間跳躍,動畫幫助我們解釋用戶從哪里來,要到哪里去。

鍵盤在 view 中滑進(jìn)滑出給了我們一個錯覺,讓我們以為它是簡單的被隱藏在屏幕下方的,并且是手機(jī)很自然的一個部分。View controller 轉(zhuǎn)場加強(qiáng)了我們的應(yīng)用程序的導(dǎo)航結(jié)構(gòu),并且給了用戶正在移向哪個方向的提示。微妙的反彈和碰撞使界面栩栩如生,并且激發(fā)出了物理的質(zhì)感。要是沒有這些的話,我們就只有一個沒有視覺修飾的干巴巴環(huán)境了。

動畫是敘述你的應(yīng)用的故事的絕佳方式,在了解動畫背后的基本原理之后,設(shè)計它們會輕松很多。

首要任務(wù)

在這篇文章 (以及這個話題中其余大多數(shù)文章) 中,我們將特別地針對 Core Animation 進(jìn)行探討。雖然你將看到的很多東西也可以用更高層級的 UIKit 的方法來完成,但是 Core Animation 將會讓你更好的理解正在發(fā)生什么。它以一種更明確的方式來描述動畫,這對這篇文章讀者以及你自己的代碼的讀者來說都非常有用。

在看動畫如何與我們在屏幕上看到的內(nèi)容交互之前,我們需要快速瀏覽一下 Core Animation 的 CALayer,這是動畫產(chǎn)生作用的地方。

你大概知道 UIView 實(shí)例,以及 layer-backed 的 NSView,修改它們的 layer 來委托強(qiáng)大的 Core Graphics 框架來進(jìn)行渲染。然而,你務(wù)必要理解,當(dāng)把動畫添加到一個 layer 時,是不直接修改它的屬性的。

取而代之,Core Animation 維護(hù)了兩個平行 layer 層次結(jié)構(gòu): model layer tree(模型層樹)presentation layer tree(表示層樹)。前者中的 layers 反映了我們能直接看到的 layers 的狀態(tài),而后者的 layers 則是動畫正在表現(xiàn)的值的近似。

實(shí)際上還有所謂的第三個 layer 樹,叫做 rendering tree(渲染樹)。因?yàn)樗鼘?Core Animation 而言是私有的,所以我們在這里不討論它。

考慮在 view 上增加一個漸出動畫。如果在動畫中的任意時刻,查看 layer 的 opacity 值,你是得不到與屏幕內(nèi)容對應(yīng)的透明度的。取而代之,你需要查看 presentation layer 以獲得正確的結(jié)果。

雖然你可能不會去直接設(shè)置 presentation layer 的屬性,但是使用它的當(dāng)前值來創(chuàng)建新的動畫或者在動畫發(fā)生時與 layers 交互是非常有用的。

通過使用 -[CALayer presentationLayer]-[CALayer modelLayer],你可以在兩個 layer 之間輕松切換。

基本動畫

可能最常見的情況是將一個 view 的屬性從一個值改變?yōu)榱硪粋€值,考慮下面這個例子:

http://wiki.jikexueyuan.com/project/objc/images/12-1.gif" alt="" />

在這里,我們讓紅色小火箭的 x-position 從 77.0 變?yōu)?455.0,剛好超過它的 parent view 的邊。為了填充所有路徑,我們需要確定我們的火箭在任意時刻所到達(dá)的位置。這通常使用線性插值法來完成:

http://wiki.jikexueyuan.com/project/objc/images/12-15.png" alt="" />

也就是說,對于動畫給定的一個分?jǐn)?shù) t,火箭的 x 坐標(biāo)就是起始點(diǎn)的 x 坐標(biāo) 77,加上一個到終點(diǎn)的距離 ?x = 378 乘以該分?jǐn)?shù)的值。

使用 CABasicAnimation,我們可以如下實(shí)現(xiàn)這個動畫:

CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @77;
animation.toValue = @455;
animation.duration = 1;

[rocket.layer addAnimation:animation forKey:@"basic"];

請注意我們要動畫的鍵路徑,也就是 position.x,實(shí)際上包含一個存儲在 position 屬性中的 CGPoint 結(jié)構(gòu)體成員。這是 Core Animation 一個非常方便的特性。請務(wù)必查看支持的鍵路徑的完整列表

然而,當(dāng)我們運(yùn)行該代碼時,我們意識到火箭在完成動畫后馬上回到了初始位置。這是因?yàn)樵谀J(rèn)情況下,動畫不會在超出其持續(xù)時間后還修改 presentation layer。實(shí)際上,在結(jié)束時它甚至?xí)粡氐滓瞥?/p>

一旦動畫被移除,presentation layer 將回到 model layer 的值,并且因?yàn)槲覀儚奈葱薷脑?layer 的 position 屬性,所以我們的飛船將重新出現(xiàn)在它開始的地方。

這里有兩種解決這個問題的方法:

第一種方法是直接在 model layer 上更新屬性。這是推薦的的做法,因?yàn)樗沟脛赢嬐耆蛇x。

一旦動畫完成并且從 layer 中移除,presentation layer 將回到 model layer 設(shè)置的值,而這個值恰好與動畫最后一個步驟相匹配。

CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @77;
animation.toValue = @455;
animation.duration = 1;

[rocket.layer addAnimation:animation forKey:@"basic"];

rocket.layer.position = CGPointMake(455, 61);

或者,你可以通過設(shè)置動畫的 fillMode 屬性為 kCAFillModeForward 以留在最終狀態(tài),并設(shè)置removedOnCompletionNO 以防止它被自動移除:

CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @77;
animation.toValue = @455;
animation.duration = 1;

animation.fillMode = kCAFillModeForward;
animation.removedOnCompletion = NO;

[rectangle.layer addAnimation:animation forKey:@"basic"];

Andy Matuschak 指出了,如果將已完成的動畫保持在 layer 上時,會造成額外的開銷,因?yàn)殇秩酒鲿ミM(jìn)行額外的繪畫工作。

值得指出的是,實(shí)際上我們創(chuàng)建的動畫對象在被添加到 layer 時立刻就復(fù)制了一份。這個特性在多個 view 中重用動畫時這非常有用。比方說我們想要第二個火箭在第一個火箭起飛不久后起飛:

CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x";
animation.byValue = @378;
animation.duration = 1;

[rocket1.layer addAnimation:animation forKey:@"basic"];
rocket1.layer.position = CGPointMake(455, 61);

animation.beginTime = CACurrentMediaTime() + 0.5;

[rocket2.layer addAnimation:animation forKey:@"basic"];
rocket2.layer.position = CGPointMake(455, 111);

設(shè)置動畫的 beginTime 為未來 0.5 秒將只會影響 rocket2,因?yàn)閯赢嬙趫?zhí)行語句 [rocket1.layer addAnimation:animation forKey:@"basic"]; 時已經(jīng)被復(fù)制了,并且之后 rocket1 也不會考慮對動畫對象的改變。

不妨看一看 David 的 關(guān)于動畫時間的一篇很棒的文章,通過它可以學(xué)習(xí)如何更精確的控制你的動畫。

我決定再使用 CABasicAnimationbyValue 屬性創(chuàng)建一個動畫,這個動畫從 presentation layer 的當(dāng)前值開始,加上 byValue 的值后結(jié)束。這使得動畫更易于重用,因?yàn)槟悴恍枰_的指定可能無法提前知道的 from-toValue 的值。

fromValue, byValuetoValue 的不同組合可以用來實(shí)現(xiàn)不同的效果,如果你需要創(chuàng)建一個可以在你的不同應(yīng)用中重用的動畫,你可以查看文檔。

多步動畫

這很容易想到一個場景,你想要為你的動畫定義超過兩個步驟,我們可以使用更通用的 CAKeyframeAnimation,而不是去鏈接多個 CABasicAnimation 實(shí)例。

關(guān)鍵幀(keyframe)使我們能夠定義動畫中任意的一個點(diǎn),然后讓 Core Animation 填充所謂的中間幀。

比方說我們正在制作我們下一個 iPhone 應(yīng)用程序上的登陸表單,我們希望當(dāng)用戶輸入錯誤的密碼時表單會晃動。使用關(guān)鍵幀動畫,看起來大概像下面這樣:

http://wiki.jikexueyuan.com/project/objc/images/12-16.gif" alt="" />

CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = @"position.x";
animation.values = @[ @0, @10, @-10, @10, @0 ];
animation.keyTimes = @[ @0, @(1 / 6.0), @(3 / 6.0), @(5 / 6.0), @1 ];
animation.duration = 0.4;

animation.additive = YES;

[form.layer addAnimation:animation forKey:@"shake"];

values 數(shù)組定義了表單應(yīng)該到哪些位置。

設(shè)置 keyTimes 屬性讓我們能夠指定關(guān)鍵幀動畫發(fā)生的時間。它們被指定為關(guān)鍵幀動畫總持續(xù)時間的一個分?jǐn)?shù)。

請注意我是如何選擇不同的值從 0 到 10 和從 10 到 -10 轉(zhuǎn)換以維持恒定的速度的。

設(shè)置 additive 屬性為 YES 使 Core Animation 在更新 presentation layer 之前將動畫的值添加到 model layer 中去。這使我們能夠?qū)λ行问降男枰碌脑刂赜孟嗤膭赢嫞覠o需提前知道它們的位置。因?yàn)檫@個屬性從 CAPropertyAnimation 繼承,所以你也可以在使用 CABasicAnimation 時使用它。

沿路徑的動畫

雖然用代碼實(shí)現(xiàn)一個簡單的水平晃動并不難,但是沿著復(fù)雜路徑的動畫就需要我們在關(guān)鍵幀的 values 數(shù)組中存儲大量 box 化的 CGPoint。 值得慶幸的是,CAKeyframeAnimation 提供了更加便利的 path 屬性作為代替。

舉個例子,我們?nèi)绾巫屢粋€ view 做圓周運(yùn)動:

http://wiki.jikexueyuan.com/project/objc/images/12-2.gif" alt="" />

CGRect boundingRect = CGRectMake(-150, -150, 300, 300);

CAKeyframeAnimation *orbit = [CAKeyframeAnimation animation];
orbit.keyPath = @"position";
orbit.path = CFAutorelease(CGPathCreateWithEllipseInRect(boundingRect, NULL));
orbit.duration = 4;
orbit.additive = YES;
orbit.repeatCount = HUGE_VALF;
orbit.calculationMode = kCAAnimationPaced;
orbit.rotationMode = kCAAnimationRotateAuto;

[satellite.layer addAnimation:orbit forKey:@"orbit"];

使用 CGPathCreateWithEllipseInRect(),我們創(chuàng)建一個圓形的 CGPath 作為我們的關(guān)鍵幀動畫的 path

使用 calculationMode 是控制關(guān)鍵幀動畫時間的另一種方法。我們通過將其設(shè)置為 kCAAnimationPaced,讓 Core Animation 向被驅(qū)動的對象施加一個恒定速度,不管路徑的各個線段有多長。將其設(shè)置為 kCAAnimationPaced 將無視所有我們已經(jīng)設(shè)置的 keyTimes。

設(shè)置 rotationMode 屬性為 kCAAnimationRotateAuto 確保飛船沿著路徑旋轉(zhuǎn)。作為對比,如果我們將該屬性設(shè)置為 nil 那動畫會是什么樣的呢。

http://wiki.jikexueyuan.com/project/objc/images/12-3.gif" alt="" />

你可以使用帶路徑的動畫來實(shí)現(xiàn)幾個有趣的效果;資深 objc.io 作者 Ole Begemann 寫了一篇文章,闡述了如何將 CAShapeLayer 與基于路徑的動畫組合起來使用,并只用幾行代碼來創(chuàng)建酷炫的繪圖動畫。

時間函數(shù)

讓我們再次來看看第一個例子:

http://wiki.jikexueyuan.com/project/objc/images/12-4.gif" alt="" />

你會發(fā)現(xiàn)我們的火箭的動畫有一些看起來非常不自然的地方。那是因?yàn)槲覀冊诂F(xiàn)實(shí)世界中看到的大部分運(yùn)動需要時間來加速或減速。對象瞬間達(dá)到最高速度,然后再立即停止往往看起來非常不自然。除非你在讓機(jī)器人跳舞,但這很少是想要的效果。

為了給我們的動畫一個存在慣性的感覺,我們可以使用我們上面提到的參數(shù)因子來進(jìn)行插值。然而,如果我們接下來需要為每個需要加速或減速的行為創(chuàng)建一個新的插值函數(shù),這將是一個很難擴(kuò)展的方法。

取而代之,常見的做法是把要進(jìn)行動畫的屬性的插值從動畫的速度中解耦出來。這樣一來,給動畫提速會產(chǎn)生一種小火箭加速運(yùn)動的效果,而不用改變我們的插值函數(shù)。

我們可以通過引入一個 時間函數(shù) (timing function) (有時也被稱為 easing 函數(shù))來實(shí)現(xiàn)這個目標(biāo)。該函數(shù)通過修改持續(xù)時間的分?jǐn)?shù)來控制動畫的速度。

http://wiki.jikexueyuan.com/project/objc/images/12-17.png" alt="" />

最簡單的 easing 函數(shù)是 linear。它在整個動畫上維持一個恒定的速度。在 Core Animation 中,這個功能由 CAMediaTimingFunction 類表示。

http://wiki.jikexueyuan.com/project/objc/images/12-5.gif" alt="" />

CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x"; 
animation.fromValue = @50;
animation.toValue = @150;
animation.duration = 1;

animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

[rectangle.layer addAnimation:animation forKey:@"basic"];

rectangle.layer.position = CGPointMake(150, 0);

Core Animation 附帶了一些 linear 之外的內(nèi)置 easing 函數(shù),如:

  • Ease in (kCAMediaTimingFunctionEaseIn):
    http://wiki.jikexueyuan.com/project/objc/images/12-6.gif" alt="" />
  • Ease out (kCAMediaTimingFunctionEaseOut):
    http://wiki.jikexueyuan.com/project/objc/images/12-7.gif" alt="" />
  • Ease in ease out (kCAMediaTimingFunctionEaseInEaseOut):
    http://wiki.jikexueyuan.com/project/objc/images/12-8.gif" alt="" />
  • 默認(rèn) (kCAMediaTimingFunctionDefault):
    http://wiki.jikexueyuan.com/project/objc/images/12-9.gif" alt="" />

在一定限度內(nèi),你也可以使用 +functionWithControlPoints:::: 創(chuàng)建自己的 easing 函數(shù)。通過傳遞 cubic Bézier 曲線的兩個控制點(diǎn)的 xy 坐標(biāo),你可以輕松的創(chuàng)建自定義 easing 函數(shù),比如我為我們的紅色小火箭選擇的那個。

這個方法因?yàn)橛腥齻€無名參數(shù)而聲名狼藉,我們并不推薦在你的 API 中使用這種蛋疼的寫法。

http://wiki.jikexueyuan.com/project/objc/images/12-10.gif" alt="" />

CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @77;
animation.toValue = @455;
animation.duration = 1;

animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.5:0:0.9:0.7];

[rocket.layer addAnimation:animation forKey:@"basic"];

rocket.layer.position = CGPointMake(150, 0);

我不打算講太多關(guān)于 Bézier 曲線的細(xì)節(jié),在計算機(jī)圖形學(xué)中,它們是創(chuàng)建平滑曲線的常用技術(shù)。你可能在基于矢量的繪圖工具,比如 Sketch 或 Adobe Illustrator 中見過它們。

http://wiki.jikexueyuan.com/project/objc/images/12-11.png" alt="" />

傳遞給 +functionWithControlPoints:::: 的值有效地控制了控制點(diǎn)的位置。所得到的定時函數(shù)將基于得到的路徑來調(diào)整動畫的速度。x 軸代表時間的分?jǐn)?shù),而 y 軸是插值函數(shù)的輸入值。

遺憾的是,由于這些部分被鎖定在 [0–1] 的范圍內(nèi),我們不可能用它來創(chuàng)建一些像預(yù)期動作 (Anticipation,一種像目標(biāo)進(jìn)發(fā)前先回退一點(diǎn),到達(dá)目標(biāo)后還過沖一會兒,見下圖) 這樣的常見效果。

我寫了一個小型庫,叫做 RBBAnimation,它包含一個允許使用 更多復(fù)雜 easing 函數(shù) 的自定義子類 CAKeyframeAnimation,包括反彈和包含負(fù)分量的 cubic Bézier 函數(shù):

http://wiki.jikexueyuan.com/project/objc/images/12-12.gif" alt="" />

RBBTweenAnimation *animation = [RBBTweenAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @50;
animation.toValue = @150;
animation.duration = 1;

animation.easing = RBBCubicBezier(0.68, -0.55, 0.735, 1.55);

http://wiki.jikexueyuan.com/project/objc/images/12-13.gif" alt="" />

RBBTweenAnimation *animation = [RBBTweenAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @50;
animation.toValue = @150;
animation.duration = 1;

animation.easing = RBBEasingFunctionEaseOutBounce;

動畫組

對于某些復(fù)雜的效果,可能需要同時為多個屬性進(jìn)行動畫。想象一下,在一個媒體播放程序中,當(dāng)切換到到隨機(jī)曲目時我們讓隨機(jī)動畫生效??雌饋砭拖裣旅孢@樣:

http://wiki.jikexueyuan.com/project/objc/images/12-14.gif" alt="" />

你可以看到,我們需要同時對上面的封面的 position,rotation 和 z-position 進(jìn)行動畫。使用 CAAnimationGroup 來動畫其中一個封面的代碼大概如下:

CABasicAnimation *zPosition = [CABasicAnimation animation];
zPosition.keyPath = @"zPosition";
zPosition.fromValue = @-1;
zPosition.toValue = @1;
zPosition.duration = 1.2;

CAKeyframeAnimation *rotation = [CAKeyframeAnimation animation];
rotation.keyPath = @"transform.rotation";
rotation.values = @[ @0, @0.14, @0 ];
rotation.duration = 1.2;
rotation.timingFunctions = @[
    [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
    [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]
];

CAKeyframeAnimation *position = [CAKeyframeAnimation animation];
position.keyPath = @"position";
position.values = @[
    [NSValue valueWithCGPoint:CGPointZero],
    [NSValue valueWithCGPoint:CGPointMake(110, -20)],
    [NSValue valueWithCGPoint:CGPointZero]
];
position.timingFunctions = @[
    [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
    [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]
];
position.additive = YES;
position.duration = 1.2;

CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
group.animations = @[ zPosition, rotation, position ];
group.duration = 1.2;
group.beginTime = 0.5;

[card.layer addAnimation:group forKey:@"shuffle"];

card.layer.zPosition = 1;

我們使用 CAAnimationGroup 得到的一個好處是可以將所有動畫作為一個對象暴露出去。如果你要在應(yīng)用程序中的多個地方用工廠對象創(chuàng)建的重用的動畫的話,這將會非常有用。

你也可以使用動畫組同時控制所有動畫組成部分的時間。

Core Animation 之外

都現(xiàn)在了,你應(yīng)該已經(jīng)聽說過 UIKit Dynamics 了,這是 iOS 7 中引入的一個物理模擬框架,它允許你使用約束和力來為 views 做動畫。與 Core Animation 不同,它與你在屏幕上看到的內(nèi)容交互更為間接,但是它的動態(tài)特性讓你可以在事先不知道結(jié)果時創(chuàng)建動畫。

Facebook 最近開源了 Paper 背后的動畫引擎 Pop。從概念上講,它介于 Core Animation 和 UIKit Dynamics 之間。它完美的使用了彈簧(spring)動畫,并且能夠在動畫運(yùn)行時操控目標(biāo)值,而無需替換它。Pop 也可以在 OS X 上使用,并且允許我們在每個 NSObject 的子類中為任意屬性進(jìn)行動畫。

擴(kuò)展閱讀

上一篇:相機(jī)工作原理下一篇:Build 工具