iOS 核心動(dòng)畫(huà)

自己做的筆記,過(guò)段時(shí)間卻又忘記了,為了盡量避免這種情況快毛,同時(shí)治服拖延種種格嗅,打算開(kāi)始多寫(xiě)寫(xiě)技術(shù)向的博客。


UIKit Animation or Core Animation?

在 iOS 中唠帝,動(dòng)畫(huà)可以分為兩個(gè)類(lèi)別:UIKit屯掖,和底層的 Core Animation。

通過(guò)最基本的 UIView.animate(withDuration:) 方法建立如下的 UIKit animation襟衰,能勝任復(fù)雜程度不高的動(dòng)畫(huà):

UIView.animate(withDuration: 0.8) {
      self.orangeBlock.frame = CGRect(x: 38, y: 70, width: 300, height: 60)
}![image.png](https://upload-images.jianshu.io/upload_images/8048781-4b3a1db18a8c7915.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

但是當(dāng)要處理不單一的屬性時(shí)贴铜,往往會(huì)出現(xiàn)一些不合預(yù)期的效果,這是因?yàn)槠偕梗?UIKit 接口創(chuàng)建的動(dòng)畫(huà)是基于視圖(view)的绍坝,它并不能直接對(duì)圖層(layer)屬性作出更改。

為了制作出能符合預(yù)期的動(dòng)畫(huà)效果瑰妄,我們就需要直接對(duì)圖層進(jìn)行控制陷嘴,這就是 Core Animation 了。

基于 Core Animation间坐,我們可以創(chuàng)建兩步的 CABasicAnimation灾挨,或者用 CAKeyframAnimation 繪制路徑等。

Layer v.s. View

圖層不是視圖的替代竹宋,而是后者的底層支持劳澄。在能滿足需求的情況下,對(duì)視圖而不是圖層進(jìn)行操作——即使用 UIKit 動(dòng)畫(huà)——是被建議的做法蜈七。

在 iOS 開(kāi)發(fā)中秒拔,通常是每一個(gè)視圖有一個(gè)對(duì)應(yīng)的圖層(稱(chēng)為 layer-backed view),圖層對(duì)象會(huì)保證兩者保持同步飒硅。然而在一些特殊情況下砂缩,這種一對(duì)一的關(guān)系不是必須的(比如多個(gè)圖層支持一個(gè)單獨(dú) UIImageView 來(lái)節(jié)省因單張圖片重復(fù)出現(xiàn)的內(nèi)存占用)。

image

基本過(guò)渡動(dòng)畫(huà) - CABasicAnimation

想要對(duì)哪一組值進(jìn)行設(shè)置三娩,知道對(duì)應(yīng)的 keyPath 就好了庵芭。

let radiusAnimation = CABasicAnimation(keyPath: "cornerRadius")
radiusAnimation.fromValue = 50
radiusAnimation.toValue = 30
radiusAnimation.duration = 3

purpleBlock.layer.add(radiusAnimation, forKey: "cornerRadius")
// 為模型圖層設(shè)置新的半徑
purpleBlock.layer.cornerRadius = 30

需要注意的一點(diǎn)是,add(_: CAAnimation, forKey:) 設(shè)置的動(dòng)畫(huà)針對(duì)的是模型圖層(model layer)而不是顯示圖層(presentation layer)雀监,當(dāng)動(dòng)畫(huà)顯示完双吆,它就被移除了,顯示圖層屬性將會(huì)恢復(fù)成初始的模型圖層屬性会前。所以好乐,為了保持對(duì)象視圖變化后的狀態(tài),要記得手動(dòng)更新瓦宜。

Core Animation 控制的圖層中包含有兩套平行的繼承樹(shù):模型圖層樹(shù)(model layer tree)和顯示圖層樹(shù)(presentation layer tree)蔚万。前者反映的是圖層狀態(tài),后者是圖層在動(dòng)畫(huà)中動(dòng)態(tài)的值临庇。如果要實(shí)時(shí)跟蹤圖層的屬性變化笛坦,要檢測(cè)的是顯示圖層的值区转。

關(guān)鍵幀動(dòng)畫(huà) - CAKeyframeAnimation

現(xiàn)在,試想我們要實(shí)現(xiàn)的動(dòng)畫(huà)超過(guò)了兩步(而這是非常常見(jiàn)的情況)版扩,那么以 fromValuetoValue 簡(jiǎn)單兩組值就顯然過(guò)于局限了废离。這時(shí)候我們用 CAKeyframeAnimation 就能任意地定義和設(shè)置幀。

let shakeAnimation = CAKeyframeAnimation(keyPath: "position.x")
shakeAnimation.values = [0, 10, -10, 10, 0]
shakeAnimation.keyTimes = [0, 0.2, 0.6, 0.8, 1]
shakeAnimation.duration = 0.4
shakeAnimation.isAdditive = true

blueBlock.layer.add(shakeAnimation, forKey: "position.x")

values 數(shù)組代表的是對(duì)象的位置礁芦,keyTimes 數(shù)組代表劃分的時(shí)間段蜻韭。

除了使用狀態(tài)數(shù)組的方式,設(shè)定路徑(CGPath)也是可以的:

let boundingRect = CGRect(x: -150, y: -150, width: 300, height: 300)
let orbitAnimation = CAKeyframeAnimation(keyPath: "position")
orbitAnimation.path = CGPath(ellipseIn: boundingRect, transform: nil)
orbitAnimation.duration = 4
orbitAnimation.isAdditive = true
orbitAnimation.repeatCount = Float.infinity
orbitAnimation.calculationMode = kCAAnimationPaced
orbitAnimation.rotationMode = kCAAnimationRotateAuto
?
greyBlock.layer.add(orbitAnimation, forKey: "position")

CAAnimation 的可重用性

一個(gè)動(dòng)畫(huà)實(shí)例由 keyPath 指定其所定義的圖層屬性類(lèi)型柿扣,因此對(duì)于該屬性(如 cornerRadius)不必再重復(fù)創(chuàng)建新實(shí)例肖方,也即是能被多個(gè)對(duì)象所共用的。

為了提高其可重用性未状,我們還可以用 byValue 來(lái)替代 CABasicAnimation 中的 toValue俯画,前者設(shè)定的是變化量。

let radiusAnimation = CABasicAnimation(keyPath: "cornerRadius")
radiusAnimation.fromValue = 50
// radiusAnimation.toValue = 30
// equals:
radiusAnimation.byValue = -20

如果同時(shí)創(chuàng)建了多個(gè)動(dòng)畫(huà)司草,可以使用 CAAnimationGroup 打包成一個(gè)動(dòng)畫(huà)組:

let aniGroup = CAAnimationGroup()
aniGroup.animations = [shakeAnimation, radiusAnimation]
aniGroup.duration = 3
?
magentaBlock.layer.add(aniGroup, forKey: "whatsoever")

Timing Functions 使動(dòng)畫(huà)流暢

為了讓動(dòng)畫(huà)看起來(lái)更自然艰垂,添加一個(gè) CAMediaTimingFunction 對(duì)象實(shí)例÷窈纾可以直接對(duì)動(dòng)畫(huà)實(shí)例設(shè)置:

// 現(xiàn)有的「淡入淡出」效果
let timingFunc1 = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
ANI_INSTANCE.timingFunction = timingFunc

也可以為 CATransaction 設(shè)置:

// 自定義函數(shù)猜憎,通過(guò) controlPoints 設(shè)置貝塞爾曲線
let timingFunc2 = CAMediaTimingFunction(controlPoints: 0.65, -0.55, 0.27, 1.55)
CATransaction.setAnimationTimingFunction(timingFunc)

CATransaction 事務(wù)機(jī)制

當(dāng)我們要進(jìn)行多組圖層操作時(shí),一個(gè)很好的習(xí)慣是把它們顯式(explicitly)合并到一個(gè)事務(wù)中搔课。

CATransaction Definition: A mechanism for grouping multiple layer-tree operations into atomic updates to the render tree.

事實(shí)上胰柑,即使我們不主動(dòng)創(chuàng)建 CATransaction 事務(wù),系統(tǒng)也會(huì)默認(rèn)給我們創(chuàng)建一個(gè)隱式(implicit)事務(wù)爬泥。顯式聲明還有個(gè)好處是柬讨,可以為事務(wù)內(nèi)部的代碼統(tǒng)一設(shè)置一些默認(rèn)值(如 duration)。嵌套的 CATransaction 是允許的袍啡。

CATransaction.begin()
CATransaction.setAnimationDuration(3)

let positionAnimation = CABasicAnimation(keyPath: "position")
positionAnimation.fromValue = [38, 484]
positionAnimation.toValue = [112, 484]
greenBlockLayer.add(positionAnimation, forKey: "position")
greenBlockLayer.position = CGPoint(x: 112, y: 484)

CATransaction.begin()
CATransaction.setAnimationDuration(1)
?
UIView.animate(withDuration: b_Interval) {
 self.greenBlock.alpha = 0.5
}
?
CATransaction.commit()
CATransaction.commit()

ref:

  1. Design Patterns on iOS using Swift - Part 1/2 - raywenderlich
  2. Design Patterns on iOS using Swift – Part 2/2 - raywenderlich
  3. 設(shè)計(jì)模式 - 菜鳥(niǎo)教程
  4. KVO - Swifter
  5. Key-Value Observing Programming Guide - Apple
  6. Is key-value observation (KVO) available in Swift? - Stack Overflow
  7. Exploring KVO alternatives with Swift - Scott Logic
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踩官,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子葬馋,更是在濱河造成了極大的恐慌卖鲤,老刑警劉巖肾扰,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件畴嘶,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡集晚,警方通過(guò)查閱死者的電腦和手機(jī)窗悯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)偷拔,“玉大人蒋院,你說(shuō)我怎么就攤上這事亏钩。” “怎么了欺旧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵姑丑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我辞友,道長(zhǎng)栅哀,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任称龙,我火速辦了婚禮留拾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鲫尊。我一直安慰自己痴柔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布疫向。 她就那樣靜靜地躺著咳蔚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸿捧。 梳的紋絲不亂的頭發(fā)上屹篓,一...
    開(kāi)封第一講書(shū)人閱讀 52,246評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音匙奴,去河邊找鬼堆巧。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泼菌,可吹牛的內(nèi)容都是我干的谍肤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼哗伯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼荒揣!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起焊刹,我...
    開(kāi)封第一講書(shū)人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤系任,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后虐块,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體俩滥,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年贺奠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霜旧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡儡率,死狀恐怖挂据,靈堂內(nèi)的尸體忽然破棺而出以清,到底是詐尸還是另有隱情,我是刑警寧澤崎逃,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布掷倔,位于F島的核電站,受9級(jí)特大地震影響个绍,放射性物質(zhì)發(fā)生泄漏今魔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一障贸、第九天 我趴在偏房一處隱蔽的房頂上張望错森。 院中可真熱鬧,春花似錦篮洁、人聲如沸涩维。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瓦阐。三九已至,卻和暖如春篷牌,著一層夾襖步出監(jiān)牢的瞬間睡蟋,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工枷颊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留戳杀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓夭苗,卻偏偏與公主長(zhǎng)得像信卡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子题造,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容