CAMediaTiming 'Protocol (9.1 layer time), camediatiming9.1
# CAMediaTiming Protocol
CAMediaTiming
The Protocol defines a set of attributes used to control the elapsed time in an animation,CALayer
AndCAAnimation
This Protocol is implemented, so the time can be controlled by any class based on a layer or an animation.
Persistence and repetition
We mentioned in chapter 8 "Explicit Animation"duration
(CAMediaTiming
),duration
IsCFTimeInterval
(SimilarNSTimeInterval
A double-precision floating point type), specifies the time for an iteration of the animation to be executed.
HereOne IterationWhat does that mean?CAMediaTiming
Another attribute is calledrepeatCount
The number of iterations of the animation. Ifduration
Is 2,repeatCount
Set it to 3.5 (three semi-iterations), and the complete animation duration will be 7 seconds.
duration
AndrepeatCount
The default value is 0. However, this does not mean that the animation duration is 0 seconds, or 0 times. Here 0 only represents the "default", that is, 0.25 seconds and 1 time, you can use a simple test to assign multiple values to these two attributes, for example, listing 9.1 and Figure 9.1 show the program results.
List 9.1 Testduration
AndrepeatCount
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 @end10 11 @implementation ViewController12 13 - (void)viewDidLoad14 {15 [super viewDidLoad];16 //add the ship17 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)enabled25 {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)hideKeyboard33 {34 [self.durationField resignFirstResponder];35 [self.repeatField resignFirstResponder];36 }37 38 - (IBAction)start39 {40 CFTimeInterval duration = [self.durationField.text doubleValue];41 float repeatCount = [self.repeatField.text floatValue];42 //animate the ship rotation43 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 controls51 [self setControlsEnabled:NO];52 }53 54 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag55 {56 //reenable controls57 [self setControlsEnabled:YES];58 }59 60 @end
View Code
Figure 9.1 demoduration
AndrepeatCount
Test Program
Another way to create a duplicate animation is to userepeatDuration
Attribute, which allows the animation to repeat a specified time, rather than a specified number of times. You even setautoreverses
Attributes (BOOL type) are automatically played back during the alternating cycle of each interval. This is useful for playing a continuous, non-repeating animation, such as opening a door and closing it (Figure 9.2 ).
Figure 9.2 swing door Animation
The code for swinging the door can be found in listing 9.2. We usedautoreverses
To automatically close the door after it is opened.repeatDuration
SetINFINITY
So the animation can be played infinitely and setrepeatCount
IsINFINITY
It also has the same effect. Note:repeatCount
AndrepeatDuration
They may conflict with each other, so you only need to specify a non-zero value for one of them. The behavior of setting non-0 values for both attributes is not defined.
Usage in listing 9.2autoreverses
Swing of attribute Implementation door
@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 relative time
Each time we talk about Core Animation, the time is relative. Each Animation has its own time, which can be accelerated, delayed, or offset independently.
beginTime
Specifies the delay time before the animation starts. The latency starts from the moment the animation is added to the visible layer. The default value is 0 (that is, the animation will be executed immediately ).
speed
Is a multiple of time. The default value is 1.0. Reducing it will slow down the layer/animation time, and increasing it will speed up. If the speed is 2.0duration
The animation with a value of 1 has actually been completed in 0.5 seconds.
timeOffset
AndbeginTime
SimilarbeginTime
The delay animation is different.timeOffset
Just let the animation go to a certain point. For example, for an animation that lasts for 1 second, SettimeOffset
0.5 means that the animation starts in half.
AndbeginTime
The difference is that,timeOffset
Not Affectedspeed
. So if youspeed
Set it to 2.0timeOffset
If the animation is set to 0.5, the animation starts from the end of the animation, because the animation within one second is actually reduced to 0.5 seconds. HowevertimeOffset
Let the animation start from the end, and it still plays a full length. This animation is just a loop and then played from the beginning.
You can use the test program in listing 9.3 to verify and setspeed
AndtimeOffset
Slider to the random value, and then click play to observe the effect (see Figure 9.3)
List 9.3 TesttimeOffset
Andspeed
Attribute
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 @end12 13 @implementation ViewController14 15 - (void)viewDidLoad16 {17 [super viewDidLoad];18 //create a path19 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 CAShapeLayer23 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 ship30 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 values36 [self updateSliders];37 }38 39 - (IBAction)updateSliders40 {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)play48 {49 //create the keyframe animation50 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
Figure 9.3 simple application to test time offset and speed
FillMode
ForbeginTime
For a non-zero animation, a State appears when the animation is added to the layer but nothing happens. Similarly,removeOnCompletion
SetNO
The animation will remain in the previous State at the end of the animation. This creates a problem. Before and after the animation starts, what is the value of the property of the animation to be set?
One possibility is that the attributes are consistent with the animation before being added, that is, the value defined in the model layer (See Chapter 7 "implicit Animation", model layer and rendering layer interpretation ).
Another possibility is to keep the frame before the animation starts, or the frame after the animation ends. This is the so-calledFillBecause the animation start and end values are used to fill in the time before and after the start.
This behavior is handed over to the developer, and it can beCAMediaTiming
OffillMode
To control.fillMode
IsNSString
Type. The following four constants are acceptable:
kCAFillModeForwards kCAFillModeBackwards kCAFillModeBoth kCAFillModeRemoved
The default value iskCAFillModeRemoved
When the animation is no longer played, the remaining three types of values specified by the layer model are displayed, either backward or backward, filling the animation status, this allows the animation to remain the starting and ending values before or after the animation.
This provides another solution to avoid returning quickly when the animation ends (see Chapter 8 ). But remember, when you use it to solve this problem, you needremoveOnCompletion
SetNO
In addition, you need to add a non-empty key to the animation, so you can remove it from the layer when no animation is required.