iOS 動畫效果:Core Animation & Facebook's pop

 

本文轉載至 http://www.cocoachina.com/ios/20151223/14739.htmlhtml

感謝原創做者分享ios

前言
相信不少人對實現 iOS 中的動畫效果都特別頭疼,每每懶得動手,功能實現不就得了,何須要那麼花哨、裝13的東西。可是看到別人的炫酷動效,心中又瘙癢不已,便下定決心學習,因而開始翻看 Core Animation、UIView動畫(實際上是對Core Animation的一種封裝)相關資料。不當心看到一羣大神正在熱烈討論,鑽一進去一看,原來是 POP(潛意識:Facebook出品必屬精品),這還學什麼Core Animation,果斷pod一個來玩玩,因而你就左手CA,右手 POP 開森地把玩起來了。git

此時,你可能已經學會了CA的基本使用方法,也對UIView動畫的便捷感到驚喜,可是不知足的你,顯然有更高的追求,POP 以其靈活的用法,豐富的動效,完整的API文檔,深得不少程序員的喜好。做爲一個有逼格的程序員,這麼流行的框架,必然是值得深刻學習的,可是你是否考慮過這樣的第三方動畫框架是否存在什麼不足。所以,做爲一個有追求的程序員,有必要來稍微深刻地探討一下 Core Animation 和 POP 不一樣點。程序員

Core Animation 工做機制github

首先咱們須要瞭解CA是如何工做的。每當咱們建立並添加動畫到 layer 時,QuartzCore 框架就會把動畫的參數打包好,而後經過 IPC (處理器)發送給名爲 backboardd 的後臺處理程序。你的應用也會發送當前展現在屏幕上的每個 layer 的信息。app

backboardd 會處理 layer 的結構體系而後經過 OpenGL 繪製出來。它還會處理你已經添加過的動畫(也能夠是視圖,由於視圖本質是包裹着 layer的)。你必定要理解的是,backboardd 使得動畫的每一幀均可以在你的應用中徹底獨立。這裏惟一的回調是動畫的開始和結束(詳見CAAnimationDelegate 協議)。你的應用徹底不會參與動畫的繪製,這些繪製徹底獨立於你的應用進程(除非你明確地在你的應用中經過動畫通用屬性要求繪製動畫幀)。這意味着你能夠繼續在主線程作其餘事情,而且不會影響到 CAAnimation 的性能。若是你阻塞了你的主線程,或者你在調試器中暫停了你的程序,你的動畫仍是會繼續執行。框架

可是你可能會有這樣的疑問:每一個 CALayer 不是還有一個 presentationLayer 屬性嗎?iphone

presentationLayer的官方解釋:性能

「While an animation is in progress, you can retrieve this object and use it to get the current values for those animations.」學習

當CAAnimation發生時,你在屏幕上看到的其實是 presentation layer 的改變。若是你訪問 presentation layer,QuartzCore 將會計算現有的幀狀態,而且使用這個幀狀態去構建 presentation layer 對象。由於動畫狀態在動畫執行期間一直處於改變,所以你將會得到近似值。

POP 工做機制

如今有不少優秀的第三方動畫庫,POP 由於其使用靈活、功能強大、文檔齊全,因此備受好評,先看一下官方介紹:

POP是一個在iOS與OS X上通用的極具擴展性的動畫引擎 它在基本的靜態動畫的基礎上增長的彈簧動畫與衰減動畫。

使之能創造出更真實更具物理性的交互動畫 POP的API能夠快速的與現有的ObjC代碼集成並能夠做用於任意對象的任意屬性。

POP是個至關成熟且久經考驗的框架 Facebook出品的使人驚歎的Paper應用中的全部動畫和效果即出自POP。

更爲詳細的介紹和使用請查看官方文檔以及裏脊串的 POP介紹與使用實踐(快速上手動畫)

POP 本質上是基於定時器的動畫庫,使用每秒 60 頻率的定時器,即時鐘頻率爲 1/60 秒(爲了匹配 iOS 顯示屏幀率),使得動畫刷新繪製頻率與屏幕刷新頻率一致。不少這類動畫庫都使用 CADisplayLink 作爲一個回調源。

一旦定時器刷新,動畫庫計算動畫的進程,這意味着動畫庫會計算那些活動的東西的狀態(一般是layer 屬性,如 bound,opactiy,transform 等)。而後動畫庫提供最新計算的值給有動畫的 layer (或者其餘對象)。最主要的區別是,layer 的狀態將會在這種狀況下改變。

因爲 layer 的一些參數已經被改變,你的應用必須經過 IPC 通知 backboardd 處理這些變化。當 backboardd 接收到變化通知(同時接收到的還有應用中的 layer 樹),它將在屏幕上重繪一切東西。這意味着,你應用中作的每個動畫幀都會傳送數據到 backboardd (即通知 backboardd ),由於 backboardd 徹底不知道 layer 發生了什麼事情。綜上,你的應用就是在這種狀況下運行動畫的。

Core Animation 和 POP 運行動畫對比

因爲 POP 是基於定時器定時刷新添加動畫的原理,那麼若是將動畫庫運行在主線程上,會因爲線程阻塞的問題致使動畫效果出現卡頓、不流暢的狀況。更爲關鍵的是,你不能將動畫效果放在子線程,由於你不能將對 view 和 layer 的操做放到主線程以外。

爲了驗證上述的觀點,我作了一個實驗,首先用CA動畫製做一個能夠旋轉的 view:

1
2
3
4
5
6
7
8
UIView *viewCA = [[UIView alloc]initWithFrame:CGRectMake(50,50, 100, 100)];
    viewCA.backgroundColor = [UIColor blueColor];
    [self.view addSubview:viewCA];
    CABasicAnimation *caAnimation = [CABasicAnimation animationWithKeyPath:@ "transform.rotation.z" ];
    caAnimation.toValue = @(M_PI);
    caAnimation.duration = 2.0;
    caAnimation.repeatCount = 500;
    [viewCA.layer addAnimation:caAnimation forKey:@ "anim" ];

再建立一個利用 POP 動畫庫製做的可旋轉 view:

1
2
3
4
5
6
7
8
9
UIView *viewPOP = [[UIView alloc]initWithFrame:
CGRectMake(CGRectGetWidth(self.view.bounds) - 100 - 50, 50, 100, 100)];
    viewPOP.backgroundColor = [UIColor yellowColor];
    [self.view addSubview:viewPOP];
    POPBasicAnimation *popAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerRotation];
    popAnimation.toValue = @(M_PI);
    popAnimation.duration = 2.0;
    popAnimation.repeatCount = 500;
    [viewPOP.layer pop_addAnimation:popAnimation forKey:@ "rotation" ];

在沒有線程阻塞的狀況下,對比兩個動畫庫的運行效果以下:

37.gif

能夠看出來雖然在沒有線程阻塞,可是 POP 的動畫在結束時有一個明顯的中止動做,是由於 POP 的動畫效果很差嗎?

答案是 timingFunction

CoreAnimation 和 POPBasicAnimation提供一樣的五種 timingFunction:

  • kCAMediaTimingFunctionLinear

  • kCAMediaTimingFunctionEaseIn

  • kCAMediaTimingFunctionEaseOut

  • kCAMediaTimingFunctionEaseInEaseOut

  • kCAMediaTimingFunctionDefault

重點說一下:kCAMediaTimingFunctionDefault(引自:iOS-Core-Animation-Advanced-Techniques(五)

它和kCAMediaTimingFunctionEaseInEaseOut很相似,可是加速和減速的過程都稍微有些慢。它和kCAMediaTimingFunctionEaseInEaseOut的區別很難察覺,多是蘋果以爲它對於隱式動畫來講更適合(而後對UIKit就改變了想法,而是使用kCAMediaTimingFunctionEaseInEaseOut做爲默認效果),雖然它的名字說是默認的,但仍是要記住當建立顯式的CAAnimation它並非默認選項(換句話說,默認的圖層行爲動畫用kCAMediaTimingFunctionDefault做爲它們的計時方法)。

若是不設置 timingFunction 屬性,那麼在使用 CA 的狀況下, timingFunction 是kCAMediaTimingFunctionLinear 的,而 POP 倒是kCAMediaTimingFunctionEaseOut ,所以咱們只要添加這麼一行代碼:

1
popAnimation.timingFunction?=?[CAMediaTimingFunction?functionWithName:kCAMediaTimingFunctionLinear];

如今再看效果:

38.gif

 

能夠看出來,在主線程沒有阻塞的狀況下,兩種動畫庫的表現並沒有差別。

如今咱們來製造一點難度,人工利用線程的 sleep 增長一個主線程阻塞:

1
2
3
4
5
6
- (void)repeatedlyBlockMainThread
{
     NSLog(@ "blocking main thread!" );
     [NSThread sleepForTimeInterval:0.25];
     [self performSelector:@selector(repeatedlyBlockMainThread) withObject:nil afterDelay:1];
}

而後再 viewDidLoad 裏面調用 :

1
[self performSelector:@selector(repeatedlyBlockMainThread) withObject:nil afterDelay:1];

如今再來看一下二者的動畫效果:

39.gif

很明顯,咱們能夠看出來,因爲添加了主線程阻塞,利用 POP 製做的動畫視圖,在每隔 1s 都會卡頓一下,而 CA 的視圖卻徹底不受主線程阻塞的影響。

總結

經過此次簡單的對比,咱們從工做機制上了解了 CA 和 POP 兩個動畫庫的基本原理,並用簡單的動畫效果對比,重現了在主線程阻塞的狀況下二者的差別,很顯然, POP 受主線程阻塞的影響很大,在使用過程當中,應避免在有可能發生主線程阻塞的狀況下使用 POP ,避免製做卡頓的動畫效果,產生很差的用戶體驗。文中提出了 POP 的這種缺點,可是 POP 畢竟是久經考驗的動畫技術,本人也正在學習中,有錯誤的地方吝請指正。

對比系列,是我的比較喜歡的一種學習方式,經過對比,找出不一樣技術的優缺點,能夠更合理地使用這些武器,俗話說:好鋼用在刀刃上,大抵如此。

相關文章
相關標籤/搜索