CALayer的模型層與展示層

前言

本文关炼,我們將深入CALayer內(nèi)部,通過簡單的CABasicAnimation動畫來探究CALayer的兩個非常重要的屬性:presentationLayer和modelLayer展哭。

從一個改變位置的動畫開始

我們可以使用CABasicAnimation來實現(xiàn)各種動畫相艇,假如我們想讓一個視圖從一個位置動畫地移動到另一個位置堕油,我們的代碼可能會這樣寫:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UIView * view = [[UIView alloc] initWithFrame:CGRectMake(80, 80, 100, 100)];
    view.backgroundColor = [UIColor blueColor];
    [self.view addSubview:view];

    // 動畫開始
    CABasicAnimation * animation = [CABasicAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 2;
    animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(80, 80)];
    animation.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 300)];
    [view.layer addAnimation:animation forKey:nil];
}

也就是指定了動畫的四要素:

  • 動什么(keyPath)
  • 動多久(duration)
  • 從什么樣子開始(fromValue)
  • 動到什么樣子(toValue)

這樣CABasicAnimation就能在duration內(nèi)對keyPath指定的屬性從fromValue到toValue之間進行插值封救。然后我們將動畫施加于view的layer,這樣它就會按照我們的四要素進行動畫了杏慰。

我們運行一下將會有如下效果:

20151223165138882.gif

注意:動畫結(jié)束后测柠,視圖又回去了?

瞎子和瘸子

從前有一個瞎子和一個瘸子缘滥,瘸子看得見路轰胁,但是不能自己走,瞎子能自己走朝扼,但是他看不見路赃阀。于是他們想到了一個辦法:瞎子背著瘸子,這樣瘸子就指揮瞎子如何走路擎颖,瞎子負責(zé)走路榛斯,每走一步,瞎子都會停下來聽從瘸子的指揮搂捧,這樣他們就不緊不慢的走向了目的地驮俗。

在CALayer內(nèi)部也有一個瞎子和一個瘸子:presentationLayer(以下簡稱P)和modelLayer(以下簡稱M)。presentationLayer負責(zé)走路(繪制內(nèi)容)允跑,而modelLayer負責(zé)看路(如何繪制)王凑。

P有這樣的特點:

1、我們看到的一切聋丝,都是P的內(nèi)容荤崇;
2、P只在下次屏幕刷新時才會進行繪制潮针。

M有這樣的特點:

1、我們我們對CALayer的各種繪圖屬性進行賦值和訪問實際上都是訪問的M的屬性倚喂,比如bounds每篷、backgroundColor瓣戚、position等;
2焦读、對這些屬性進行賦值子库,不會影響P,也就是不會影響繪制內(nèi)容矗晃。
你可以把M理解成一個隱身的家伙仑嗅,只有P才能感知它的存在。
那么為什么我們對M的屬性進行賦值张症,“與此同時”視圖的顯示狀態(tài)就會發(fā)生改變呢仓技?

如果我們用t0表示我們對屬性進行賦值的時刻,t1表示下次屏幕刷新的時刻俗他,t0到t1之間的間隔相當(dāng)短(小于1/60秒)脖捻,我們的人眼根本察覺不到這樣的間隔時間存在,所以我們的感覺就是:賦值和界面繪制是同時進行的兆衅。然而實際上地沮,賦值和界面繪制之間有一個t1-t0的時間間隔。

所以總結(jié)以上信息羡亩,我們可以知道P和M是這樣進行交互的:當(dāng)下次屏幕刷新信號到來時(屏幕重繪時)摩疑,P為了繪制內(nèi)容到屏幕上,它會去找M要各種它需要的用來繪制的屬性畏铆,然后用這些屬性的信息來繪制雷袋。

直到屏幕刷新信號到來才進行繪制,這種方式能夠極大提高繪制效率及志∑牛考慮這樣一種情況,如果連續(xù)寫了以下三行代碼:

view.frame = …; 
view.backgounrdColor = …; 
view.center = …;速侈,

如果每次賦值就會導(dǎo)致屏幕的重繪率寡,這樣就會有三次重繪,也就是GPU會執(zhí)行三次繪制操作倚搬。而如果我們先把繪制信息存起來冶共,當(dāng)需要繪制的時候(屏幕刷新)再用這些信息去重繪,GPU就只會執(zhí)行一次繪制操作每界。如果不能理解這樣做的優(yōu)勢捅僵,我們將會在實戰(zhàn)篇講述如何提高性能的時候再次帶大家了解繪圖機制,也就是你看到的一切究竟是如何被顯示出來的眨层。

上面三行代碼庙楚,只是對M的屬性賦值了而已,沒有任何的繪制操作趴樱,當(dāng)下次屏幕刷新信號到來時馒闷,P才會去找M要這些屬性酪捡,進行一次繪制。執(zhí)行這三次賦值(執(zhí)行三行代碼)所需的時間遠遠小于兩次屏幕刷新的間隔纳账,所以這樣三行代碼是一定能夠在下次繪制開始前執(zhí)行完的逛薇。也就是說,當(dāng)我們寫了這三行代碼后疏虫,視圖的顯示內(nèi)容不會改變?nèi)斡婪#粫淖円淮巍?/p>

如果我們繼續(xù)按照瞎子和瘸子的理論進行下去,P背著M卧秘,如果我們在下次屏幕刷新前(P走下一步之前)對M進行了三次操作:把M移動到點1呢袱,把M移動到點2,把M移動到點3斯议。那么下次屏幕刷新時产捞,P會直接問M“你在哪啊”,M說我在點3呢哼御,你這樣這樣走過來坯临,于是P就屁顛屁顛的按照M的指揮跑到點3去了,它是不會先跑到點1再跑到點2再跑到點3的恋昼。

CAAnimation對presentationLayer的控制

P這個瞎子背著M這個瘸子看靠,他們互幫互助,完成各種顯示的邏輯液肌。每當(dāng)屏幕刷新的時候挟炬,如果沒有其他的信息告訴P它應(yīng)該如何繪制,那么P就只能去問M嗦哆,然后回到M的狀態(tài)谤祖。

這樣一個平衡會在一個CAAnimation被加到CALayer上后會被打破。當(dāng)我們調(diào)用了layer的addAnimation方法后老速,一個CAAnimation(以下簡稱A)就控制了P粥喜,就相當(dāng)于A一腳把M從P身上踹了下來然后自己爬了上去(不要想歪 )。

現(xiàn)在P要如何顯示就完全由A來控制了橘券,因為A被指定了在duration中從fromValue變到toValue额湘。所以在動畫的持續(xù)時間內(nèi),M被丟在了原地旁舰,P則背著A到處跑(畢竟P是個瞎子锋华,根本不知道他腦袋頂上的家伙是誰),每一步該如何走都是A通過插值計算出來的(通過duration箭窜、from毯焕、to就能計算任意時刻P的狀態(tài)了)。

動畫結(jié)束后磺樱,默認情況A就被移除了纳猫,這時候P身上啥也沒有了紧阔。那么在下一次屏幕刷新的時候,沒有其他的信息告訴P它應(yīng)該如何繪制续担,所以它就又開口了“M你在哪里啊”,M說“勞資在這里”(在動畫過程當(dāng)中它就沒被動過活孩,除非你手動設(shè)置了M的值)物遇,“你怎么跑那里去了”,“要你管憾儒,快給我滾過來”(真是一對好CP)询兴。于是P就屁顛屁顛的滾到M那里去了,所以動畫結(jié)束后起趾,你會看到視圖又回去了诗舰。

如果想要P在動畫結(jié)束后就停在當(dāng)前狀態(tài)而不回到M的狀態(tài),我們就需要給A設(shè)置兩個屬性训裆,一個是A.removedOnCompletion = NO;表示動畫結(jié)束后A依然影響著P眶根,另一個是A.fillMode = kCAFillModeForwards,(關(guān)于fillMode到底干了什么我們將在下一章詳細進行講解)边琉,這兩行代碼將會讓A控制住P在動畫結(jié)束后保持不變(不會回到M的狀態(tài))属百,我們一開始的代碼就會寫成這個樣子:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UIView * view = [[UIView alloc] initWithFrame:CGRectMake(80, 80, 100, 100)];
    view.backgroundColor = [UIColor blueColor];
    [self.view addSubview:view];
    CABasicAnimation * animation = [CABasicAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 2;
    animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(80, 80)];
    animation.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 300)];
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    [view.layer addAnimation:animation forKey:nil];
}
20151223165652502.gif

模型與顯示的同步

我們可以通過設(shè)置removedOnCompletion = NO以及fillMode來讓動畫結(jié)束后保持狀態(tài),但是此時我們的P和M不同步变姨,我們看到的P是toValue的狀態(tài)族扰,而M則還是自己原來的狀態(tài)。舉個栗子定欧,我們初始化一個view渔呵,它的狀態(tài)為1,我們給它的layer加個動畫砍鸠,from是0扩氢,to是2,設(shè)置fillMode為kCAFillModeForewards睦番,則動畫結(jié)束后P的狀態(tài)是2类茂,M的狀態(tài)是1,這可能會導(dǎo)致一些問題出現(xiàn)托嚣。比如你點P所在的位置點不動巩检,因為響應(yīng)點擊的是M。所以我們應(yīng)該讓P和M同步示启。

在CABasicAnimation的文檔中寫了這樣一句話:如果不設(shè)置toValue兢哭,則CABasicAnimation會從fromValue到M的值之間進行插值。也就是說夫嗓,如果不設(shè)置toValue迟螺,則CABasicAnimation會把M的值作為toValue冲秽,所以我們就可以在加動畫的時候只設(shè)置fromValue,再手動修改M的值到你想要動畫停止的那個狀態(tài)就保持同步了矩父。

我們可以擴展到任意的CAAnimation對象锉桑,比如CAKeyFrameAnimation,都可以通過設(shè)置M的值到動畫結(jié)束的狀態(tài)來保持P和M的同步窍株。

所以我們的代碼可能就會寫成這樣:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UIView * view = [[UIView alloc] initWithFrame:CGRectMake(80, 80, 100, 100)];
    view.center = CGPointMake(200, 300);
    view.backgroundColor = [UIColor blueColor];
    [self.view addSubview:view];

    CABasicAnimation * animation = [CABasicAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 2;
    animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(80, 80)];
//    animation.toValue = [NSValue valueWithCGPoint:CGPointMake(200, 300)];
//    animation.removedOnCompletion = NO;
//    animation.fillMode = kCAFillModeForwards; 
    [view.layer addAnimation:animation forKey:nil];
}

當(dāng)我們寫view.center = CGPointMake(200, 300);的時候民轴,只是對M賦值,再次強調(diào)球订,它不會影響P的顯示后裸,而當(dāng)P想要顯示的時候,它已經(jīng)被A控制了冒滩,所以P會從一開始就在80,80那里然后動畫地移動到200,300微驶,不會出現(xiàn)先在200,300那里閃一下的情況。
運行一下开睡,效果就是這樣:

20151223165812366.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末因苹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子士八,更是在濱河造成了極大的恐慌容燕,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婚度,死亡現(xiàn)場離奇詭異蘸秘,居然都是意外死亡,警方通過查閱死者的電腦和手機蝗茁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進店門醋虏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哮翘,你說我怎么就攤上這事颈嚼。” “怎么了饭寺?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵阻课,是天一觀的道長。 經(jīng)常有香客問我艰匙,道長限煞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任员凝,我火速辦了婚禮署驻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己旺上,他們只是感情好瓶蚂,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宣吱,像睡著了一般窃这。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上征候,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天钦听,我揣著相機與錄音,去河邊找鬼倍奢。 笑死,一個胖子當(dāng)著我的面吹牛垒棋,可吹牛的內(nèi)容都是我干的卒煞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼叼架,長吁一口氣:“原來是場噩夢啊……” “哼畔裕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乖订,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤扮饶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后乍构,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甜无,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年哥遮,在試婚紗的時候發(fā)現(xiàn)自己被綠了岂丘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡眠饮,死狀恐怖奥帘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仪召,我是刑警寧澤寨蹋,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站扔茅,受9級特大地震影響已旧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咖摹,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一评姨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦吐句、人聲如沸胁后。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攀芯。三九已至,卻和暖如春文虏,著一層夾襖步出監(jiān)牢的瞬間侣诺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工氧秘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留年鸳,地道東北人。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓丸相,卻偏偏與公主長得像搔确,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子灭忠,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

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