在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

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

動(dòng)畫(huà)解釋

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

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

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

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

首要任務(wù)

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

在看動(dòng)畫(huà)如何與我們?cè)谄聊簧峡吹降膬?nèi)容交互之前,我們需要快速瀏覽一下 Core Animation 的 CALayer,這是動(dòng)畫(huà)產(chǎn)生作用的地方。

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

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

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

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

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

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

基本動(dòng)畫(huà)

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

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

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

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

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

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

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

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

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

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

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

這里有兩種解決這個(gè)問(wèn)題的方法:

第一種方法是直接在 model layer 上更新屬性。這是推薦的的做法,因?yàn)樗沟脛?dòng)畫(huà)完全可選。

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

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);

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

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 指出了,如果將已完成的動(dòng)畫(huà)保持在 layer 上時(shí),會(huì)造成額外的開(kāi)銷(xiāo),因?yàn)殇秩酒鲿?huì)去進(jìn)行額外的繪畫(huà)工作。

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

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è)置動(dòng)畫(huà)的 beginTime 為未來(lái) 0.5 秒將只會(huì)影響 rocket2,因?yàn)閯?dòng)畫(huà)在執(zhí)行語(yǔ)句 [rocket1.layer addAnimation:animation forKey:@"basic"]; 時(shí)已經(jīng)被復(fù)制了,并且之后 rocket1 也不會(huì)考慮對(duì)動(dòng)畫(huà)對(duì)象的改變。

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

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

fromValue, byValuetoValue 的不同組合可以用來(lái)實(shí)現(xiàn)不同的效果,如果你需要?jiǎng)?chuàng)建一個(gè)可以在你的不同應(yīng)用中重用的動(dòng)畫(huà),你可以查看文檔。

多步動(dòng)畫(huà)

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

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

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

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)鍵幀動(dòng)畫(huà)發(fā)生的時(shí)間。它們被指定為關(guān)鍵幀動(dòng)畫(huà)總持續(xù)時(shí)間的一個(gè)分?jǐn)?shù)。

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

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

沿路徑的動(dòng)畫(huà)

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

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

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)建一個(gè)圓形的 CGPath 作為我們的關(guān)鍵幀動(dòng)畫(huà)的 path。

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

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

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

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

時(shí)間函數(shù)

讓我們?cè)俅蝸?lái)看看第一個(gè)例子:

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

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

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

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

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

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

最簡(jiǎn)單的 easing 函數(shù)是 linear。它在整個(gè)動(dòng)畫(huà)上維持一個(gè)恒定的速度。在 Core Animation 中,這個(gè)功能由 CAMediaTimingFunction 類(lèi)表示。

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ù)。通過(guò)傳遞 cubic Bézier 曲線的兩個(gè)控制點(diǎn)的 xy 坐標(biāo),你可以輕松的創(chuàng)建自定義 easing 函數(shù),比如我為我們的紅色小火箭選擇的那個(gè)。

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

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ì)算機(jī)圖形學(xué)中,它們是創(chuàng)建平滑曲線的常用技術(shù)。你可能在基于矢量的繪圖工具,比如 Sketch 或 Adobe Illustrator 中見(jiàn)過(guò)它們。

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

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

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

我寫(xiě)了一個(gè)小型庫(kù),叫做 RBBAnimation,它包含一個(gè)允許使用 更多復(fù)雜 easing 函數(shù) 的自定義子類(lèi) 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;

動(dòng)畫(huà)組

對(duì)于某些復(fù)雜的效果,可能需要同時(shí)為多個(gè)屬性進(jìn)行動(dòng)畫(huà)。想象一下,在一個(gè)媒體播放程序中,當(dāng)切換到到隨機(jī)曲目時(shí)我們讓隨機(jī)動(dòng)畫(huà)生效??雌饋?lái)就像下面這樣:

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

你可以看到,我們需要同時(shí)對(duì)上面的封面的 position,rotation 和 z-position 進(jìn)行動(dòng)畫(huà)。使用 CAAnimationGroup 來(lái)動(dòng)畫(huà)其中一個(gè)封面的代碼大概如下:

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 得到的一個(gè)好處是可以將所有動(dòng)畫(huà)作為一個(gè)對(duì)象暴露出去。如果你要在應(yīng)用程序中的多個(gè)地方用工廠對(duì)象創(chuàng)建的重用的動(dòng)畫(huà)的話,這將會(huì)非常有用。

你也可以使用動(dòng)畫(huà)組同時(shí)控制所有動(dòng)畫(huà)組成部分的時(shí)間。

Core Animation 之外

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

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

擴(kuò)展閱讀

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