CoreAnimation初探(二) —— 初識CALayer與動畫

上篇提到CoreAnimation是在圖形層之上,我們先看下CoreAnimation在框架中的位置:


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

圖層類是CoreAnimation的基礎,它提供了一套抽象概念。CALayer是整個圖層類的基礎,它是所有核心動畫圖層類的父類。

本篇主要談談CALayer(圖層類)和CAAnimation(動畫類)的內容和類關系,以及它們實現的一個重要的協議CAMediaTiming。


1.CALayer

為什么UIView要加一層Layer來負責顯示呢?我們知道QuartzCore是跨iOS和macOS平臺的,而UIView屬于UIKit是iOS開發使用的,在macOS中對應AppKit里的NSView。這是因為macOS是基于鼠標指針操作的系統,與iOS的多點觸控有本質的區別。雖然iOS在交互上與macOS有所不同,但在顯示層面卻可以使用同一套技術。

// UIView中與layer相關的屬性方法
@interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, CALayerDelegate>

/* layer的class,默認為CALayer,可以用自定義的layer */
#if UIKIT_DEFINE_AS_PROPERTIES
@property(class, nonatomic, readonly) Class layerClass;                        // default is [CALayer class]. Used when creating the underlying layer for the view.
#else
+ (Class)layerClass;                        // default is [CALayer class]. Used when creating the underlying layer for the view.
#endif

/* view的leyer,view是layer的代理 */
@property(nonatomic,readonly,strong)                 CALayer  *layer;              // returns view's layer. Will always return a non-nil value. view is layer's delegate

可以想象下我們看到的view實際上都是它的layer,我們先通過CALayer中幾何相關的屬性來認識下它:

  • bounds:圖層的bounds是一個CGRect的值,指定圖層的大小(bounds.size)和原點(bounds.origin)
/* The bounds of the layer. Defaults to CGRectZero. Animatable. */
@property CGRect bounds;
  • position:指定圖層的位置(相對于父圖層而言)
/* The position in the superlayer that the anchor point of the layer's
 * bounds rect is aligned to. Defaults to the zero point. Animatable. */
@property CGPoint position;
  • anchorPoint:錨點指定了position在當前圖層中的位置,坐標范圍0~1。position點的值是相對于父圖層的,而這個position到底位于當前圖層的什么地方,是由錨點決定的。(默認在圖層的中心,即錨點為(0.5,0.5) )
/* Defines the anchor point of the layer's bounds rect, as a point in
 * normalized layer coordinates - '(0, 0)' is the bottom left corner of
 * the bounds rect, '(1, 1)' is the top right corner. Defaults to
 * '(0.5, 0.5)', i.e. the center of the bounds rect. Animatable. */
@property CGPoint anchorPoint;
  • transform:指定圖層的幾何變換,類型為上篇說過的CATransform3D
/* A transform applied to the layer relative to the anchor point of its
 * bounds rect. Defaults to the identity transform. Animatable. */
@property CATransform3D transform;

這些屬性的注釋最后都有一句Animatable,就是說我們可以通過改變這些屬性來實現動畫。默認地,我們修改這些屬性都會導致圖層從舊值動畫顯示為新值,稱為隱式動畫。(注意,修改UIView自帶的layer(root layer)是沒有隱式動畫的)

還有一個屬性比較特殊,那就是layer的frame:

/* Unlike NSView, each Layer in the hierarchy has an implicit frame
 * rectangle, a function of the `position', `bounds', `anchorPoint',
 * and `transform' properties. When setting the frame the `position'
 * and `bounds.size' are changed to match the given frame. */
@property CGRect frame;

注意到frame的注釋里面是沒有Animatable的。事實上,我們可以理解為圖層的frame并不是一個真實的屬性:當我們讀取frame時,會根據圖層position、bounds、anchorPoint和transform的值計算出它的frame;而當我們設置frame時,圖層會根據anchorPoint改變position和bounds。也就是說frame本身并沒有被保存。

圖層不但給自己提供可視化的內容和管理動畫,而且充當了其他圖層的容器類,構建圖層層次結構

圖層樹類似于UIView的層次結構,一個view實例擁有父視圖(superView)和子視圖(subView);同樣一個layer也有父圖層(superLayer)和子圖層(subLayer)。我們可以直接在view的layer上添加子layer達到一些顯示效果,但這些單獨的layer無法像UIView那樣進行交互響應。

2.CAAnimation

CALayer提供以下方法來管理動畫:


- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;

- (void)removeAllAnimations;

- (void)removeAnimationForKey:(NSString *)key;

- (nullable NSArray<NSString *> *)animationKeys;

- (nullable CAAnimation *)animationForKey:(NSString *)key;

CAAnimation是動畫基類,我們常用的CABasicAnimation和CAKeyframeAnimation都繼承于CAPropertyAnimation即屬性動畫。屬性動畫通過改變layer的可動畫屬性(位置、大小等)實現動畫效果。CABasicAnimation可以看做有兩個關鍵幀的CAKeyframeAnimation,通過插值形成一條通過各關鍵幀的動畫路徑。但CABasicAnimation更加靈活一些:

@interface CABasicAnimation : CAPropertyAnimation
/* 我們可以通過下面三個值來規定CABasicAnimation的動畫起止狀態
 * 這三個屬性都是可選的,通常給定其中一個或者兩個,以下是官方建議的使用方式
 * * 給定fromValue和toValue,將在兩者之間進行插值 *
 * * 給定fromValue和byValue,將在fromValue和fromValue+byValue之間插值 *
 * * 給定byValue和toValue,將在toValue-byValue和toValue之間插值 *
 * * 僅給定fromValue,將在fromValue和當前值之間插值 *
 * * 僅給定toValue,將在當前值和toValue之間插值 *
 * * 僅給定byValue,將在當前值和當前值+byValue之間插值 * */
@property(nullable, strong) id fromValue;
@property(nullable, strong) id toValue;
@property(nullable, strong) id byValue;

@end

在CAKeyframeAnimation中,除了給定各關鍵幀之外還可以指定關鍵幀之間的時間和時間函數:

@interface CAKeyframeAnimation : CAPropertyAnimation

@property(nullable, copy) NSArray *values;
@property(nullable, copy) NSArray<NSNumber *> *keyTimes;
/* 時間函數有線性、淡入、淡出等簡單效果,還可以指定一條三次貝塞爾曲線 */
@property(nullable, copy) NSArray<CAMediaTimingFunction *> *timingFunctions;

@end

到這我們已經能夠感覺到,所謂動畫實際上就是在不同的時間顯示不同畫面,時間在走進而形成連續變化的效果。所以,動畫的關鍵就是對時間的控制。

3.CAMediaTiming

CAMediaTiming是CoreAnimation中一個非常重要的協議,CALayer和CAAnimation都實現了它來對時間進行管理。協議定義了8個屬性,通過它們來控制時間,這些屬性大都見名知意:

@protocol CAMediaTiming

@property CFTimeInterval beginTime;
@property CFTimeInterval duration;
@property float speed;
/* timeOffset時間的偏移量,用它可以實現動畫的暫停、繼續等效果 */
@property CFTimeInterval timeOffset;
@property float repeatCount;
@property CFTimeInterval repeatDuration;
/* autoreverses為true時時間結束后會原路返回,默認為false */
@property BOOL autoreverses;
/* fillMode填充模式,有4種,見下 */
@property(copy) NSString *fillMode;

@end

NSString * const kCAFillModeForwards;  // 向前填充,結束后保持狀態
NSString * const kCAFillModeBackwards;  // 向后填充,開始之前維持開始狀態
NSString * const kCAFillModeBoth;  // 前后填充,同時保持前后狀態
NSString * const kCAFillModeRemoved;  // 無填充,結束后移除,fillMode默認為這個值

下面這張圖形象的說明了這些屬性是如何靈活的進行動畫時間控制的:

動畫時間控制

這張圖的出處,以及有關CAMediaTiming的時間控制,可以參考Controlling Animation TimingiOS CoreAnimation專題——原理篇(四)動畫時間控制,里面介紹的非常詳細。需要注意的是,CALayer也實現了CAMediaTiming協議,也就是說如果我們將layer的speed設置為2,那么加到這個layer上的動畫都會以兩倍速執行。


本篇從圖層、動畫和時間控制的關系上簡單認識了CALayer、屬性動畫和動畫時間控制,了解屬性動畫是根據時間在各關鍵幀之間進行插值,隨時間連續改變layer的某動畫屬性來實現的。

參考資料

對CoreGraphics和QuartzCore的理解
Quartz 2D 繪圖技術
Core Animation基礎介紹、簡單使用CALayer以及多種動畫效果
通過CALayer讓你的APP動起來
Controlling Animation Timing
iOS CoreAnimation專題——原理篇(四)動畫時間控制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容

  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺ios動畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,537評論 6 30
  • 在iOS中隨處都可以看到絢麗的動畫效果,實現這些動畫的過程并不復雜,今天將帶大家一窺iOS動畫全貌。在這里你可以看...
    F麥子閱讀 5,130評論 5 13
  • Core Animation其實是一個令人誤解的命名。你可能認為它只是用來做動畫的,但實際上它是從一個叫做Laye...
    小貓仔閱讀 3,747評論 1 4
  • 書寫的很好,翻譯的也棒!感謝譯者,感謝感謝! iOS-Core-Animation-Advanced-Techni...
    錢噓噓閱讀 2,308評論 0 6
  • 曾經信誓旦旦揚言 曾經看厭煩惱無數 曾經不屑還被情困 如今也非昔日光景 如今也是難以啟齒 如今也是曾經厭惡 倒不如...
    阿火moete閱讀 253評論 0 0