Core Animation

話外:之前自己做的一個demo帖池,用到了CABasicAnimation,覺得動畫太神奇了,現(xiàn)在才開
始認(rèn)真學(xué)習(xí)Core Animation颁股!這篇文章只寫了目前自己需要掌握的部分锋爪。

原博地址:CALayer與iOS動畫 講解及使用

1.關(guān)于Core Animation

CoreAnimation是蘋果提供的一套基于繪圖的動畫框架组题,下圖是官方文檔中給出的體系結(jié)構(gòu)。


Core Animation.png

從圖中可以看出膳沽,最底層是圖形硬件(GPU);上層是OpenGL和CoreGraphics让禀,提供一些接口來訪問GPU挑社;再上層的CoreAnimation在此基礎(chǔ)上封裝了一套動畫的API。最上面的UIKit屬于應(yīng)用層巡揍,處理與用戶的交互痛阻。所以,學(xué)習(xí)CoreAnimation也會涉及一些圖形學(xué)的知識腮敌,了解這些有助于我們更順手的使用以及更高效的解決問題阱当。

2.初識CALayer

CoreAnimation屬于QuartzCore框架俏扩,Quartz原本是macOS的Darwin核心之上的繪圖技術(shù)。在iOS中弊添,我們所看到的視圖UIView是通過QuartzCore中的CALayer顯示出來的录淡,我們討論的動畫效果也是加在這個CALayer上的。

  • 下面主要的內(nèi)容是:
    CALayer(圖層類)和CAAnimation(動畫類)的內(nèi)容和關(guān)系
    以及他們實(shí)現(xiàn)的一個重要協(xié)議CAMediaTiming

CALayer圖層類是CoreAnimation的基礎(chǔ)油坝,它提供了一套抽象概念嫉戚。CALayer是整個圖層類的基礎(chǔ),它是所有核心動畫圖層的父類澈圈。

1.CALayer

為什么UIView要加一層Layer來負(fù)責(zé)顯示呢彬檀?我們知道QuartzCore是跨iOS和macOS平臺的,而UIView屬于UIKit是iOS開發(fā)使用的瞬女,在macOS中對應(yīng)AppKit里的NSView窍帝。這是因?yàn)閙acOS是基于鼠標(biāo)指針操作的系統(tǒng),與iOS的多點(diǎn)觸控有本質(zhì)的區(qū)別拆魏。雖然iOS在交互上與macOS有所不同盯桦,但在顯示層面卻可以使用同一套技術(shù)。
每一個UIView都有個屬性layer渤刃、默認(rèn)為CALayer類型拥峦,也可以使用自定義的Layer

/* view的leyer,view是layer的代理 */
@property(nonatomic,readonly,strong) CALayer  *layer;

可以想象我們看到的View其實(shí)都是它的layer卖子,下面我們通過CALayer中的集合相關(guān)的屬性來認(rèn)識它:

  • bounds:圖層的bounds是一個CGRect的值略号,指定圖層的大小(bounds.size)和原點(diǎn)(bounds.origin)

  • position:指定圖層的位置(相對于父圖層而言)

  • anchorPoint:錨點(diǎn)指定了position在當(dāng)前圖層中的位置洋闽,坐標(biāo)范圍0~1玄柠。position點(diǎn)的值是相對于父圖層的,而這個position到底位于當(dāng)前圖層的什么地方诫舅,是由錨點(diǎn)決定的羽利。(默認(rèn)在圖層的中心,即錨點(diǎn)為(0.5,0.5) )刊懈。
    劃重點(diǎn):設(shè)置完錨點(diǎn)后这弧,都要在設(shè)置一下position的值

  • transform:指定圖層的幾何變換,類型為上篇說過的CATransform3D

這些屬性的注釋最后都有一句Animatable虚汛,就是說我們可以通過改變這些屬性來實(shí)現(xiàn)動畫匾浪。默認(rèn)地,我們修改這些屬性都會導(dǎo)致圖層從舊值動畫顯示為新值卷哩,稱為隱式動畫蛋辈。
注意到frame的注釋里面是沒有Animatable的。事實(shí)上将谊,我們可以理解為圖層的frame并不是一個真實(shí)的屬性:當(dāng)我們讀取frame時冷溶,會根據(jù)圖層position渐白、bounds、anchorPoint和transform的值計算出它的frame挂洛;而當(dāng)我們設(shè)置frame時礼预,圖層會根據(jù)anchorPoint改變position和bounds。也就是說frame本身并沒有被保存虏劲。

圖層不但給自己提供可視化的內(nèi)容和管理動畫托酸,而且充當(dāng)了其他圖層的容器類,構(gòu)建圖層層次結(jié)構(gòu)

圖層樹類似于UIView的層次結(jié)構(gòu)柒巫,一個view實(shí)例擁有父視圖(superView)和子視圖(subView)励堡;同樣一個layer也有父圖層(superLayer)和子圖層(subLayer)。我們可以直接在view的layer上添加子layer達(dá)到一些顯示效果堡掏,但這些單獨(dú)的layer無法像UIView那樣進(jìn)行交互響應(yīng)应结。

2.CAMediaTiming

CAMediaTiming是CoreAnimation中一個非常重要的協(xié)議,CALayer和CAAnimation都實(shí)現(xiàn)了它來對時間進(jìn)行管理泉唁。
協(xié)議定義了8個屬性鹅龄,通過它們來控制時間,這些屬性大都見名知意:

@protocol CAMediaTiming
/* 開始的時間*/
@property CFTimeInterval beginTime;
/* 持續(xù)時間*/
@property CFTimeInterval duration;
@proterty float speed;
/* timeOffset時間的偏移量亭畜,用它可以實(shí)現(xiàn)動畫的暫停扮休、繼續(xù)等效果*/
@proterty CFTimeInterval timeOffset;
/* 重復(fù)次數(shù)*/
@property float repeatCount;
@property CFTimeInterval repeatDuration;
/* autoreverses為true時時間結(jié)束后會原路返回,默認(rèn)為false */
@property BOOL autoreverses;
/* fillMode填充模式拴鸵,有4種玷坠,見下 */
@property(copy) NSString *fillMode;
@end

3.UIView動畫

UIView提供了一系列UIViewAnimationWithBlocks,我們只需要把改變可動畫屬性的代碼放在animations的block中即可實(shí)現(xiàn)動畫效果劲藐,比如:

[UIView animateWithDuration:1 animations:^(void){        
          if (_testView.bounds.size.width > 150)
          {
              _testView.bounds = CGRectMake(0, 0, 100, 100);
          }
          else
          {
              _testView.bounds = CGRectMake(0, 0, 200, 200);
          }
      } completion:^(BOOL finished){
          NSLog(@"%d",finished);
      }];
其中  我自己有用到這個方法:   彈性動畫(彈簧)
/* Performs `animations` using a timing curve described by   the motion of a spring. When `dampingRatio` is 1, the 
animation will smoothly decelerate to its final model values without oscillating. Damping ratios less than 1 will oscillate 
more and more before coming to a complete stop. You can use the initial spring velocity to specify how fast the object at the 
end of the simulated spring was moving before it was attached. It's a unit coordinate system, where 1 is defined as 
travelling the total animation distance in a second. So if you're changing an object's position by 200pt in this animation, and 
you want the animation to behave as if the object was moving at 100pt/s before the animation started, you'd pass 0.5. You'll 
typically want to pass 0 for the velocity. 
使用彈簧運(yùn)動描述的時間曲線來執(zhí)行“動畫”八堡。當(dāng)“dampingRatio”為1時,動畫將平穩(wěn)地減速到最終的模型值聘芜,而不會出現(xiàn)振蕩兄渺。阻尼比小于1會在完全停止前不斷振蕩。您可以
使用初始的彈簧速度來指定模擬彈簧的末端物體在被連接之前的移動速度汰现。它是一個單位坐標(biāo)系挂谍,其中1被定義為在一秒鐘內(nèi)移
動整個動畫距離。如果你在這個動畫中把一個對象的位置改變了200pt服鹅,你想讓動畫在動畫開始前以100pt/s的速度運(yùn)行凳兵,你就會
通過0百新。5企软。你通常要通過0來表示速度。*/ 
[UIView animateWithDuration:1.0 delay:0 usingSpringWithDamping:0.5 initialSpringVelocity:0.8 options:UIViewAnimationOptionCurveEaseOut  animations:^{
        // 添加發(fā)布按鈕
        [self addPubButton];
    } completion:nil];

4.展示層(presentationLayer)和模型層(modelLayer)

我們知道UIView動畫其實(shí)是layer層做的饭望,而view是對layer的一層封裝仗哨,我們對view的bounds等這些屬性的操作其實(shí)都是對它所持有的layer進(jìn)行操作形庭,我們做一個簡單的實(shí)驗(yàn)—在UIView動畫的block中改變view的bounds后,分別查看下view和layer的bounds的實(shí)際值:

_testView.bounds = CGRectMake(0, 0, 100, 100);
    [UIView animateWithDuration:1 animations:^(void){
        _testView.bounds = CGRectMake(0, 0, 200, 200);
    } completion:nil];
賦值完成后我們分別打印view厌漂,layer的bounds:
(lldb) po _textView.bounds
(origin = (x = 0, y = 0), size = (width = 200, height = 200))

(lldb) po _textView.layer.bounds
(origin = (x = 0, y = 0), size = (width = 200, height = 200))

都已經(jīng)變成了(200,200)萨醒,這是肯定的,之前已經(jīng)驗(yàn)證過set view的bounds實(shí)際上就是set 它的layer的bounds苇倡「恢剑可動畫不是layer實(shí)現(xiàn)的么?layer也已經(jīng)到達(dá)終點(diǎn)了旨椒,它是怎么將動畫展示出來的呢晓褪?
這里就要提到CALayer的兩個實(shí)例方法presentationLayer和modelLayer:

@interface CALayer : NSObject <NSCoding, CAMediaTiming>

/* 以下參考官方api注釋 */
/* presentationLayer
 * 返回一個layer的拷貝,如果有任何活動動畫時综慎,包含當(dāng)前狀態(tài)的所有l(wèi)ayer屬性
 * 實(shí)際上是逼近當(dāng)前狀態(tài)的近似值涣仿。
 * 嘗試以任何方式修改返回的結(jié)果都是未定義的。
 * 返回值的sublayers 示惊、mask好港、superlayer是當(dāng)前l(fā)ayer的這些屬性的presentationLayer
 */
- (nullable instancetype)presentationLayer;

/* modelLayer
 * 對presentationLayer調(diào)用,返回當(dāng)前模型值米罚。
 * 對非presentationLayer調(diào)用钧汹,返回本身。
 * 在生成表示層的事務(wù)完成后調(diào)用此方法的結(jié)果未定義阔拳。
 */
- (instancetype)modelLayer;

從注釋不難看出崭孤,這個presentationLayer即是我們看到的屏幕上展示的狀態(tài),而modelLayer就是我們設(shè)置完立即生效的真實(shí)狀態(tài)

總結(jié)

  • 到這里糊肠,CALayer動畫的原理基本清晰了辨宠,當(dāng)有動畫加入時,presentationLayer會不斷的(從按某種插值或逼近得到的動畫路徑上)取值來進(jìn)行展示货裹,當(dāng)動畫結(jié)束被移除時則取modelLayer的狀態(tài)展示嗤形。這也是為什么我們用CABasicAnimation時,設(shè)定當(dāng)前值為fromValue時動畫執(zhí)行結(jié)束又會回到起點(diǎn)的原因弧圆,實(shí)際上動畫結(jié)束并不是回到起點(diǎn)而是到了modelLayer的位置赋兵。
    雖然我們可以使用fillMode控制它結(jié)束時保持狀態(tài),但這種方法在動畫執(zhí)行完之后并沒有將動畫從渲染樹中移除(因?yàn)槲覀冃枰O(shè)置animation.removedOnCompletion = NO才能讓fillMode生效)搔预。如果我們想讓動畫停在終點(diǎn)霹期,更合理的辦法是一開始就將layer設(shè)置成終點(diǎn)狀態(tài),其實(shí)前文提到的UIView的block動畫就是這么做的拯田。
  • UIView持有一個CALayer負(fù)責(zé)展示历造,view是這個layer的delegate。改變view的屬性實(shí)際上是在改變它持有的layer的屬性,layer屬性發(fā)生改變時會調(diào)用代理方法actionForLayer: forKey: 來得知此次變化是否需要動畫吭产。對同一個屬性疊加動畫會從當(dāng)前展示狀態(tài)開始疊加并最終停在modelLayer的真實(shí)位置侣监。
  • CALayer內(nèi)部控制兩個屬性presentationLayer和modelLayer,modelLayer為當(dāng)前l(fā)ayer真實(shí)的狀態(tài)臣淤,presentationLayer為當(dāng)前l(fā)ayer在屏幕上展示的狀態(tài)橄霉。presentationLayer會在每次屏幕刷新時更新狀態(tài),如果有動畫則根據(jù)動畫獲取當(dāng)前狀態(tài)進(jìn)行繪制邑蒋,動畫移除后則取modelLayer的狀態(tài)姓蜂。

CALayer和UIView

CALayer和UIView的區(qū)別

1.UIView是UIKit的(只能iOS使用),CALayer是QuartzCore的(iOS和mac os通用)
2.UIView繼承UIResponder,CALayer繼承NSObject,UIView比CALayer多了一個事件處理的功能医吊,也就是說覆糟,CALayer不能處理用戶的觸摸事件,而UIView可以
3.UIView來自CALayer遮咖,是CALayer的高層實(shí)現(xiàn)和封裝滩字,UIView的所有特性來源于CALayer支持
4.CABasicAnimation,CAAnimation御吞,CAKeyframeAnimation等動畫類都需要加到CALayer上

其實(shí)UIView之所以能顯示在屏幕上麦箍,完全是因?yàn)樗鼉?nèi)部的一個圖層,在創(chuàng)建UIView對象時陶珠,UIView內(nèi)部會自動創(chuàng)建一個圖層(即CALayer對象)挟裂,通過UIView的layer屬性可以訪問這個層。

@property(nonatomic,readonly,retain) CALayer *layer;

當(dāng)UIView需要顯示到屏幕上時揍诽,會調(diào)用drawRect:方法進(jìn)行繪圖诀蓉,并且會將所有內(nèi)容繪制在自己的圖層上,繪圖完畢后暑脆,系統(tǒng)會將圖層拷貝到屏幕上渠啤,于是就完成了UIView的顯示
換句話說,UIView本身不具備顯示的功能添吗,是它內(nèi)部的層才有顯示功能

CoreAnimation

CAAnimation:核心動畫的基礎(chǔ)類沥曹,不能直接使用,負(fù)責(zé)動畫運(yùn)行時間碟联、速度的控制妓美,本身實(shí)現(xiàn)了CAMediaTiming協(xié)議。
CAPropertyAnimation:屬性動畫的基類(通過屬性進(jìn)行動畫設(shè)置鲤孵,注意是可動畫屬性)壶栋,不能直接使用。

CAAnimationGroup:動畫組普监,動畫組是一種組合模式設(shè)計贵试,可以通過組合動畫組來進(jìn)行所有動畫行為的統(tǒng)一控制丧没,組中的所有動畫效果可以并發(fā)執(zhí)行。

CATransition:轉(zhuǎn)場動畫锡移,通過濾鏡進(jìn)行動畫效果設(shè)置。

CABasicAnimation:基礎(chǔ)動畫漆际,通過屬性修改進(jìn)行動畫參數(shù)控制淆珊,只有初始狀態(tài)和結(jié)束狀態(tài)。

CAKeyframeAnimation:關(guān)鍵幀動畫奸汇,同樣是通過屬性進(jìn)行動畫參數(shù)控制施符,但是同基礎(chǔ)動畫不同的是它可以有多個狀態(tài)控制。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末擂找,一起剝皮案震驚了整個濱河市戳吝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贯涎,老刑警劉巖听哭,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異塘雳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門筐乳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來野宜,“玉大人,你說我怎么就攤上這事妻顶∷嵩保” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵讳嘱,是天一觀的道長幔嗦。 經(jīng)常有香客問我,道長沥潭,這世上最難降的妖魔是什么崭添? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮叛氨,結(jié)果婚禮上呼渣,老公的妹妹穿的比我還像新娘。我一直安慰自己寞埠,他們只是感情好屁置,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著仁连,像睡著了一般蓝角。 火紅的嫁衣襯著肌膚如雪阱穗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天使鹅,我揣著相機(jī)與錄音揪阶,去河邊找鬼。 笑死患朱,一個胖子當(dāng)著我的面吹牛鲁僚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播裁厅,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼冰沙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了执虹?” 一聲冷哼從身側(cè)響起拓挥,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袋励,沒想到半個月后侥啤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茬故,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年愿棋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片均牢。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡糠雨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出徘跪,到底是詐尸還是另有隱情甘邀,我是刑警寧澤,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布垮庐,位于F島的核電站松邪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏哨查。R本人自食惡果不足惜逗抑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寒亥。 院中可真熱鬧邮府,春花似錦、人聲如沸溉奕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽加勤。三九已至仙辟,卻和暖如春同波,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叠国。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工未檩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粟焊。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓冤狡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吆玖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

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