iOS Core Animation系列之CADisplayLink
一直以來都想好好學習下CoreAnimation,奈何涉及的東西太多,想要一次性全部搞定時間上不允許,以後會斷斷續續的補全。最近項目裡用到了CADisplayLink,就順便花點時間看了看。一、簡介1、所在架構CADisplayLink和其它CoreAnimation類一樣,都是在QuartzCore.framework裡。2、功能CADisplayLink最主要的特徵是能提供一個周期性的調用我們賦給它的selector的機制,從這點上看它很像定時器NSTimer。3、使用方式[objc]view plaincopy
- -(void)startDisplayLink
- {
- self.displayLink=[CADisplayLinkdisplayLinkWithTarget:self
- selector:@selector(handleDisplayLink:)];
- [self.displayLinkaddToRunLoop:[NSRunLoopcurrentRunLoop]
- forMode:NSDefaultRunLoopMode];
- }
-
- -(void)handleDisplayLink:(CADisplayLink*)displayLink
- {
- //dosomething
- }
-
- -(void)stopDisplayLink
- {
- [self.displayLinkinvalidate];
- self.displayLink=nil;
- } 當把CADisplayLink對象add到runloop中後,selector就能被周期性調用,類似於NSTimer被啟動了;執行invalidate操作時,CADisplayLink對象就會從runloop中移除,selector調用也隨即停止,類似於NSTimer的invalidate方法。 二、特性下面結合NSTimer來介紹CADisplayLink,與NSTimer不同的地方有:1、原理不同CADisplayLink是一個能讓我們以和螢幕重新整理率同步的頻率將特定的內容畫到螢幕上的定時器類。CADisplayLink以特定模式註冊到runloop後,每當螢幕顯示內容重新整理結束的時候,runloop就會向CADisplayLink指定的target發送一次指定的selector訊息,CADisplayLink類對應的selector就會被調用一次。NSTimer以指定的模式註冊到runloop後,每當設定的周期時間到達後,runloop會向指定的target發送一次指定的selector訊息。2、周期設定方式不同iOS裝置的螢幕重新整理頻率(FPS)是60Hz,因此CADisplayLink的selector預設調用周期是每秒60次,這個周期可以通過frameInterval屬性設定,CADisplayLink的selector每秒調用次數=60/frameInterval。比如當frameInterval設為2,每秒調用就變成30次。因此,CADisplayLink周期的設定方式略顯不便。NSTimer的selector調用周期可以在初始化時直接設定,相對就靈活的多。3、精確度不同iOS裝置的螢幕重新整理頻率是固定的,CADisplayLink在正常情況下會在每次重新整理結束都被調用,精確度相當高。NSTimer的精確度就顯得低了點,比如NSTimer的觸發時間到的時候,runloop如果在忙於別的調用,觸發時間就會延遲到下一個runloop周期。更有甚者,在OS X v10.9以後為了盡量避免在NSTimer觸發時間到了而去中斷當前處理的任務,NSTimer新增了tolerance屬性,讓使用者可以設定可以容忍的觸發的時間範圍。4、使用場合從原理上不難看出,CADisplayLink使用場合相對專一,適合做介面的不停重繪,比如視頻播放的時候需要不停地擷取下一幀用於介面渲染。NSTimer的使用範圍要廣泛的多,各種需要單次或者迴圈定時處理的任務都可以使用。 三、重要屬性下面不完整的列出了CADisplayLink的幾個重要屬性:1、frameInterval可讀可寫的NSInteger型值,標識間隔多少幀調用一次selector方法,預設值是1,即每幀都調用一次。官方文檔中強調,當該值被設定小於1時,結果是不可預知的。2、duration唯讀CFTimeInterval值,表示兩次螢幕重新整理之間的時間間隔。需要注意的是,該屬性在target的selector被首次調用以後才會被賦值。selector的調用間隔時間計算方式是:時間=duration×frameInterval。現存的iOS裝置螢幕的FPS都是60Hz,這一點可以從CADisplayLink的duration屬性看出來。duration的值都是0.166666…,即1/60。儘管如此,我們並沒法確定蘋果不會改變FPS,如果以後某一天將FPS提升到了120Hz了怎麼辦呢?這時,你設定了frameInterval屬性值為2期望每秒重新整理30次,卻發現每秒重新整理了60次,結果可想而知,出於安全考慮,還是先根據duration判斷螢幕的FPS再去使用CADisplayLink。3、timestamp唯讀CFTimeInterval值,表示螢幕顯示的上一幀的時間戳記,這個屬性通常被target用來計算下一幀中應該顯示的內容。
列印timestamp值,其樣式類似於: [objc]view plaincopy
- 179699.631584 雖然名為時間戳記,但這和常見的unix時間戳記差異很大,事實上這是CoreAnimation使用的時間格式。每個CALayer都有一個本地時間(CALayer本地時間的具體作用會在後續文章中說明),可以擷取當前CALayer的本地時間並列印: [objc]view plaincopy
- CFTimeIntervallocalLayerTime=[myLayerconvertTime:CACurrentMediaTime()fromLayer:nil];
- NSLog("localLayerTime:%f",localLayerTime); 四、注意iOS並不能保證能以每秒60次的頻率調用回調方法,這取決於:
1、CPU的空閑程度如果CPU忙於其它計算,就沒法保證以60HZ執行螢幕的繪製動作,導致跳過若干次調用回調方法的機會,跳過次數取決CPU的忙碌程度。
2、執行回調方法所用的時間如果執行回調時間大於重繪每幀的間隔時間,就會導致跳過若干次回調調用機會,這取決於執行時間長短。 五、參考文檔1、官方文檔https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/Reference/Reference.html2、官方使用CADisplayLink播放視頻的例子https://developer.apple.com/library/ios/samplecode/AVBasicVideoOutput/Introduction/Intro.html