1. 程式人生 > >Layer中自定義屬性的動畫

Layer中自定義屬性的動畫

預設情況下,CALayer 及其子類的絕大部分標準屬性都可以執行動畫,無論是新增一個CAAnimation 到 Layer(顯式動畫),亦或是為屬性指定一個動作然後修改它(隱式動畫)。

但有時候我們希望能同時為好幾個屬性新增動畫,使它們看起來像是一個動畫一樣;或者,我們需要執行的動畫不能通過使用標準 Layer 屬性動畫來實現。

在本文中,我們將討論如何子類化 CALayer 並新增我們自己的屬性,以便比較容易地建立那些如果以其他方式實現起來會很麻煩的動畫效果。

一般說來,我們希望新增到 CALayer 的子類上的可動畫屬性有三種類型:

  • 能間接動畫 Layer (或其子類)的一個或多個標準屬性的屬性。
  • 能觸發 Layer 背後的影象(即 contents 屬性)重繪的屬性。
  • 不涉及 Layer 重繪或對任何已有屬性執行動畫的屬性。

間接屬性動畫

能間接修改其它標準 Layer 屬性的自定義屬性是這些選項中最簡單的。它們僅僅只是自定義 setter 方法。然後將它們的輸入轉換為適用於建立動畫的一個或多個不同的值。

如果被我們設定的屬性已經預設好標準動畫,那我們完全不需要編寫任何實際的動畫程式碼,因為我們修改這些屬性後,它們就會繼承任何被配置在當前 CATransaction 上的動畫設定,並且自動執行動畫。

換句話說,即使 CALayer 不知道如何對我們自定義的屬性進行動畫,它依然能對因自定義屬性被改變而引起的其它可見副作用進行動畫,而這恰好就是我們所需要的。

為了演示這種方法,讓我們來建立一個簡單的模擬時鐘,之後我們可以使用被宣告為 NSDate 型別time 屬性來設定它的時間。我會將從建立一個靜態的時鐘面盤開始。這個時鐘包含三個CAShapeLayer 例項 —— 一個用於時鐘面盤的圓形 Layer 和兩個用於時針和分針的長方形 Sublayer。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @interface ClockFace: CAShapeLayer @property (nonatomic, strong) NSDate *time; @end @interface ClockFace () // 私有屬性 @property (nonatomic, strong) CAShapeLayer *hourHand; @property (nonatomic, strong) CAShapeLayer *minuteHand; @end @implementation ClockFace - (id)init { if ((self = [super init])) { self.bounds = CGRectMake(0, 0, 200, 200); self.path = [UIBezierPath bezierPathWithOvalInRect:self.bounds].CGPath; self.fillColor = [UIColor whiteColor].CGColor; self.strokeColor = [UIColor blackColor].CGColor; self.lineWidth = 4; self.hourHand = [CAShapeLayer layer]; self.hourHand.path = [UIBezierPath bezierPathWithRect:CGRectMake(-2, -70, 4, 70)].CGPath; self.hourHand.fillColor = [UIColor blackColor].CGColor; self.hourHand.position = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); [self addSublayer:self.hourHand]; self.minuteHand = [CAShapeLayer layer]; self.minuteHand.path = [UIBezierPath bezierPathWithRect:CGRectMake(-1, -90, 2, 90)].CGPath; self.minuteHand.fillColor = [UIColor blackColor].CGColor; self.minuteHand.position = CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); [self addSublayer:self.minuteHand];