CAMedia提姆ing`协议(9.1 图层时间)

#CAMediaTiming`协议

CAMediaTiming磋商定义了在一段动画内用来支配逝去岁月的性能的汇聚,CALayerCAAnimation都落到实处了那一个协议,所以时间能够被擅自基于一个图层或者一段动画的类控制。

各处和重新

咱俩在第八章“显式动画”中简单关联过durationCAMediaTiming的性能之一),duration是一个CFTimeInterval的类型(类似于NSTimeInterval的一种双精度浮点类型),对即将举办的动画片的一次迭代指定了光阴。

这里的两回迭代是何等意思呢?CAMediaTiming除此以外还有一个属性叫做repeatCount,代表动画重复的迭代次数。若是duration是2,repeatCount设为3.5(八个半迭代),那么完整的卡通片时长将是7秒。

durationrepeatCount默许都是0。但那不意味着动画时长为0秒,或者0次,那里的0仅仅代表了“默许”,也就是0.25秒和1次,你可以用一个简单的测试来品尝为那七个属性赋五个值,如清单9.1,图9.1展示了程序的结果。

清单9.1 测试durationrepeatCount

图片 1图片 2

 1 @interface ViewController ()
 2 
 3 @property (nonatomic, weak) IBOutlet UIView *containerView;
 4 @property (nonatomic, weak) IBOutlet UITextField *durationField;
 5 @property (nonatomic, weak) IBOutlet UITextField *repeatField;
 6 @property (nonatomic, weak) IBOutlet UIButton *startButton;
 7 @property (nonatomic, strong) CALayer *shipLayer;
 8 
 9 @end
10 
11 @implementation ViewController
12 
13 - (void)viewDidLoad
14 {
15     [super viewDidLoad];
16     //add the ship
17     self.shipLayer = [CALayer layer];
18     self.shipLayer.frame = CGRectMake(0, 0, 128, 128);
19     self.shipLayer.position = CGPointMake(150, 150);
20     self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
21     [self.containerView.layer addSublayer:self.shipLayer];
22 }
23 
24 - (void)setControlsEnabled:(BOOL)enabled
25 {
26     for (UIControl *control in @[self.durationField, self.repeatField, self.startButton]) {
27         control.enabled = enabled;
28         control.alpha = enabled? 1.0f: 0.25f;
29     }
30 }
31 
32 - (IBAction)hideKeyboard
33 {
34     [self.durationField resignFirstResponder];
35     [self.repeatField resignFirstResponder];
36 }
37 
38 - (IBAction)start
39 {
40     CFTimeInterval duration = [self.durationField.text doubleValue];
41     float repeatCount = [self.repeatField.text floatValue];
42     //animate the ship rotation
43     CABasicAnimation *animation = [CABasicAnimation animation];
44     animation.keyPath = @"transform.rotation";
45     animation.duration = duration;
46     animation.repeatCount = repeatCount;
47     animation.byValue = @(M_PI * 2);
48     animation.delegate = self;
49     [self.shipLayer addAnimation:animation forKey:@"rotateAnimation"];
50     //disable controls
51     [self setControlsEnabled:NO];
52 }
53 
54 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
55 {
56     //reenable controls
57     [self setControlsEnabled:YES];
58 }
59 
60 @end

View Code

图片 3

图9.1 演示durationrepeatCount的测试程序

成立重复动画的另一种方法是行使repeatDuration性能,它让动画重复一个点名的时间,而不是点名次数。你甚至设置一个叫作autoreverses的性质(BOOL类型)在历次间隔交替循环进度中活动回看。那对于播放一段连接非循环的卡通片很有用,例如打开一扇门,然后关上它(图9.2)。

图片 4

 

图9.2 摆动门的卡通片

对门进行摆动的代码见清单9.2。我们用了autoreverses来使门在开拓后活动关闭,在那边我们把repeatDuration设置为INFINITY,于是动画无限循环播放,设置repeatCountINFINITY也有雷同的成效。注意repeatCountrepeatDuration可能会互相争辩,所以你若是对其中一个指定非零值。对八个属性都安装非0值的行事并未被定义。

清单9.2 使用autoreverses属性落成门的摇晃

 

图片 5图片 6

@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

View Code

相对时间

老是研讨到Core
Animation,时间都是争辨的,每个动画都有它和谐讲述的时刻,可以独立地加快,延时或者摇头。

beginTime指定了动画开始从前的的延迟时间。那里的推迟从动画添加到可知图层的那一刻起先测量,默许是0(就是说动画会霎时执行)。

speed是一个时间的翻番,默许1.0,裁减它会减速图层/动画的时日,伸张它会加连忙度。假诺2.0的进程,那么对于一个duration为1的动画,实际上在0.5秒的时候就已经到位了。

timeOffsetbeginTime看似,不过和充实beginTime致使的推移动画分化,扩充timeOffset只是让动画快进到某一点,例如,对于一个相连1秒的动画来说,设置timeOffset为0.5表示动画将从大体上的地点开始。

beginTime分歧的是,timeOffset并不受speed的震慑。所以假设您把speed设为2.0,把timeOffset安装为0.5,那么你的动画片将从动画最终竣工的地方初阶,因为1秒的动画实际上被浓缩到了0.5秒。然则固然采取了timeOffset让动画片从为止的地点初步,它依然播放了一个整机的时长,这一个动画仅仅是循环了一圈,然后从头起始播放。

可以用清单9.3的测试程序验证一下,设置speedtimeOffset滑块到任意的值,然后点击播放来考察效果(见图9.3)

清单9.3 测试timeOffsetspeed属性

 

图片 7图片 8

 1 @interface ViewController ()
 2 
 3 @property (nonatomic, weak) IBOutlet UIView *containerView;
 4 @property (nonatomic, weak) IBOutlet UILabel *speedLabel;
 5 @property (nonatomic, weak) IBOutlet UILabel *timeOffsetLabel;
 6 @property (nonatomic, weak) IBOutlet UISlider *speedSlider;
 7 @property (nonatomic, weak) IBOutlet UISlider *timeOffsetSlider;
 8 @property (nonatomic, strong) UIBezierPath *bezierPath;
 9 @property (nonatomic, strong) CALayer *shipLayer;
10 
11 @end
12 
13 @implementation ViewController
14 
15 - (void)viewDidLoad
16 {
17     [super viewDidLoad];
18     //create a path
19     self.bezierPath = [[UIBezierPath alloc] init];
20     [self.bezierPath moveToPoint:CGPointMake(0, 150)];
21     [self.bezierPath addCurveToPoint:CGPointMake(300, 150) controlPoint1:CGPointMake(75, 0) controlPoint2:CGPointMake(225, 300)];
22     //draw the path using a CAShapeLayer
23     CAShapeLayer *pathLayer = [CAShapeLayer layer];
24     pathLayer.path = self.bezierPath.CGPath;
25     pathLayer.fillColor = [UIColor clearColor].CGColor;
26     pathLayer.strokeColor = [UIColor redColor].CGColor;
27     pathLayer.lineWidth = 3.0f;
28     [self.containerView.layer addSublayer:pathLayer];
29     //add the ship
30     self.shipLayer = [CALayer layer];
31     self.shipLayer.frame = CGRectMake(0, 0, 64, 64);
32     self.shipLayer.position = CGPointMake(0, 150);
33     self.shipLayer.contents = (__bridge id)[UIImage imageNamed: @"Ship.png"].CGImage;
34     [self.containerView.layer addSublayer:self.shipLayer];
35     //set initial values
36     [self updateSliders];
37 }
38 
39 - (IBAction)updateSliders
40 {
41     CFTimeInterval timeOffset = self.timeOffsetSlider.value;
42     self.timeOffsetLabel.text = [NSString stringWithFormat:@"%0.2f", timeOffset];
43     float speed = self.speedSlider.value;
44     self.speedLabel.text = [NSString stringWithFormat:@"%0.2f", speed];
45 }
46 
47 - (IBAction)play
48 {
49     //create the keyframe animation
50     CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
51     animation.keyPath = @"position";
52     animation.timeOffset = self.timeOffsetSlider.value;
53     animation.speed = self.speedSlider.value;
54     animation.duration = 1.0;
55     animation.path = self.bezierPath.CGPath;
56     animation.rotationMode = kCAAnimationRotateAuto;
57     animation.removedOnCompletion = NO;
58     [self.shipLayer addAnimation:animation forKey:@"slide"];
59 }
60 
61 @end

View Code

图片 9

图9.3 测试时间偏移和速度的简要的应用程序

fillMode

对于beginTime非0的一段动画来说,会油但是生一个当动画添加到图层上但怎么样也没暴发的情况。类似的,removeOnCompletion被安装为NO的动画片将会在动画停止的时候如故维持此前的情况。那就生出了一个问题,当动画发轫以前和卡通片为止之后,被装置动画的性质将会是怎么值吗?

一种可能是性质和卡通没被加上之前保持一致,也就是在模型图层定义的值(见第七章“隐式动画”,模型图层和显示图层的分解)。

另一种可能是维持动画伊始从前那一帧,或者动画片截至以后的那一帧。那就是所谓的填充,因为动画开首和了结的值用来填充发轫以前和竣事之后的小运。

那种表现就付给开发者了,它可以被CAMediaTimingfillMode来控制。fillMode是一个NSString品类,可以接受如下四种常量:

kCAFillModeForwards 
kCAFillModeBackwards 
kCAFillModeBoth 
kCAFillModeRemoved

默认是kCAFillModeRemoved,当动画不再播放的时候就突显图层模型指定的值剩下的两种档次向前,向后仍旧即上前又向后去填充动画状态,使得动画在上马前照旧终止后如故维持初步和告竣那一刻的值。

这就对防止在动画停止的时候疾速重回提供另一种方案(见第八章)。可是切记了,当用它来化解这么些题目的时候,必要把removeOnCompletion设置为NO,别的索要给动画添加一个非空的键,于是可以在不须求动画的时候把它从图层上移除。

 

 

相关文章