時間和空間最大的區別在於,時間不能被複用 -- 弗斯特梅里克git
在上面兩章中,咱們探討了能夠用CAAnimation
和它的子類實現的多種圖層動畫。動畫的發生是須要持續一段時間的,因此計時對整個概念來講相當重要。在這一章中,咱們來看看CAMediaTiming
,看看Core Animation是如何跟蹤時間的。github
CAMediaTiming
協議 CAMediaTiming 協議定義了在一段動畫內用來控制逝去時間的屬性的集合,CALayer
和CAAnimation
都實現了這個協議,因此時間能夠被任意基於一個圖層或者一段動畫的類控制。app
咱們在第八章「顯式動畫」中簡單提到過duration
(CAMediaTiming
的屬性之一),duration
是一個CFTimeInterval
的類型(相似於NSTimeInterval
的一種雙精度浮點類型),對將要進行的動畫的一次迭代指定了時間。ide
這裏的一次迭代是什麼意思呢?CAMediaTiming
另外還有一個屬性叫作 repeatCount ,表明動畫重複的迭代次數。若是 duration 是2, repeatCount 設爲3.5(三個半迭代),那麼完整的動畫時長將是7秒。測試
duration
和repeatCount
默認都是0。但這不意味着動畫時長爲0秒,或者0次,這裏的0僅僅表明了「默認」,也就是0.25秒和1次,你能夠用一個簡單的測試來嘗試爲這兩個屬性賦多個值,如清單9.1,圖9.1展現了程序的結果。動畫
清單9.1 測試duration
和repeatCount
atom
@interface ViewController () @property (nonatomic, weak) IBOutlet UIView *containerView; @property (nonatomic, weak) IBOutlet UITextField *durationField; @property (nonatomic, weak) IBOutlet UITextField *repeatField; @property (nonatomic, weak) IBOutlet UIButton *startButton; @property (nonatomic, strong) CALayer *shipLayer;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //add the ship self.shipLayer = [CALayer layer]; self.shipLayer.frame = CGRectMake(0, 0, 128, 128); self.shipLayer.position = CGPointMake(150, 150); self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage; [self.containerView.layer addSublayer:self.shipLayer]; }- (void)setControlsEnabled:(BOOL)enabled { for (UIControl *control in @[self.durationField, self.repeatField, self.startButton]) { control.enabled = enabled; control.alpha = enabled? 1.0f: 0.25f; } }- (IBAction)hideKeyboard { [self.durationField resignFirstResponder]; [self.repeatField resignFirstResponder]; }- (IBAction)start { CFTimeInterval duration = [self.durationField.text doubleValue]; float repeatCount = [self.repeatField.text floatValue]; //animate the ship rotation CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"transform.rotation"; animation.duration = duration; animation.repeatCount = repeatCount; animation.byValue = @(M_PI * 2); animation.delegate = self; [self.shipLayer addAnimation:animation forKey:@"rotateAnimation"]; //disable controls [self setControlsEnabled:NO]; }- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { //reenable controls [self setControlsEnabled:YES]; }@end
圖9.1 演示duration
和repeatCount
的測試程序spa
建立重複動畫的另外一種方式是使用 repeatDuration 屬性,它讓動畫重複一個指定的時間,而不是指定次數。你甚至設置一個叫作 autoreverses 的屬性(BOOL類型)在每次間隔交替循環過程當中自動回放。這對於播放一段連續非循環的動畫頗有用,例如打開一扇門,而後關上它(圖9.2)。code
圖9.2 擺動門的動畫orm
對門進行擺動的代碼見清單9.2。咱們用了autoreverses
來使門在打開後自動關閉,在這裏咱們把repeatDuration
設置爲INFINITY
,因而動畫無限循環播放,設置repeatCount
爲INFINITY
也有一樣的效果。注意repeatCount
和repeatDuration
可能會相互衝突,因此你只要對其中一個指定非零值。對兩個屬性都設置非0值的行爲沒有被定義。
清單9.2 使用autoreverses
屬性實現門的搖擺
@interface ViewController () @property (nonatomic, weak) UIView *containerView;@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; //add the door CALayer *doorLayer = [CALayer layer]; doorLayer.frame = CGRectMake(0, 0, 128, 256); doorLayer.position = CGPointMake(150 - 64, 150); doorLayer.anchorPoint = CGPointMake(0, 0.5); doorLayer.contents = (__bridge id)[UIImage imageNamed: @"Door.png"].CGImage; [self.containerView.layer addSublayer:doorLayer]; //apply perspective transform CATransform3D perspective = CATransform3DIdentity; perspective.m34 = -1.0 / 500.0; self.containerView.layer.sublayerTransform = perspective; //apply swinging animation CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"transform.rotation.y"; animation.toValue = @(-M_PI_2); animation.duration = 2.0; animation.repeatDuration = INFINITY; animation.autoreverses = YES; [doorLayer addAnimation:animation forKey:nil]; }@end