Recently, I learned the knowledge related to iOS animation. When I learned the temporary and restore control of animation, I did not quite understand the concepts such as timeOffset, beginTime and fillMode, so I consulted the materials and learned one.
The official documentation gives the following code for suspending and resuming layer animation:
-(void)pauseLayer:(CALayer*)layer { CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; Layer. Speed = 0.0; layer.timeOffset = pausedTime; } -(void)resumeLayer:(CALayer*)layer { CFTimeInterval pausedTime = [layer timeOffset]; Layer. Speed = 1.0; Layer. The timeOffset = 0.0; Layer. BeginTime = 0.0; CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; layer.beginTime = timeSincePause; }Copy the code
Meng made a point:
1. What is CACurrentMediaTime()?
2. What does the layer object call convertTime: fromLayer:? Why do you do that?
3. What is timeOffset? Pause animation why do I set this?
4. What is beginTime? Why set beginTime twice? Set it to 0 the first time and to timeSincePause the second time?
5. What is a timeSincePause?
IOS time
mach_absolute_time()
Mach_absolute_time () may be used less often, but it’s a very important concept.
Describing absolute time requires a property value that varies uniformly to describe time, and the CPU’s clock cycles are just such a property. The tick value can be used to describe the time, and mach_Absolute_time () returns the number of ticks the CPU has already run. This tick number can be converted into seconds or nanoseconds after a certain amount of conversion, which is directly related to time.
The tick count, however, restarts every time the phone restarts and pauses when the iPhone’s lock screen goes to sleep.
Mach_absolute_time () is not affected by system time, only by device restart and hibernation behavior.
CACurrentMediaTime()
CACurrentMediaTime() may be in contact with more students, but let’s take a look at the official definition:
/* Returns the current CoreAnimation absolute time. This is the result of * calling mach_absolute_time () and converting the units to seconds. */ CFTimeInterval CACurrentMediaTime (void)Copy the code
CACurrentMediaTime() is the result of converting the CPU tick number of mach_Absolute_time () above to seconds. The following code:
double mediaTime = CACurrentMediaTime();
NSLog(@"CACurrentMediaTime: %f", mediaTime);Copy the code
Returns the total number of seconds the device has been running (not counting device hibernation) since startup. Another API can return the same value:
NSTimeInterval systemUptime = [[NSProcessInfo processInfo] systemUptime];
NSLog(@"systemUptime: %f", systemUptime);Copy the code
CACurrentMediaTime() is also not affected by system time, only by device restart and hibernation behavior.
IOS animation timing
Timing calculations for iOS animations are key to understanding the code above. CAMediaTiming protocol timeOffset annotation, official given the following formula:
t = (tp – begin) * speed + offset
T is the time point of the animation to be calculated
Tp is the time point of the parent layer, which can be considered as absolute time for easy understanding and increases as time goes by.
Begin, speed, and offset are the animation properties beginTime, speed, and timeOffset.
Now try to pause and resume the animation according to the above formula
Pause animation
By default, speed is 1, begin is 0, and offset is 0. Plug into the equation and you get
t = tp
If you want to pause the animation, of course, you need to set speed to 0.
But if speed is equal to 0, this equation doesn’t work. Put speed equal to 0 into the equation above, and you get
t = offset
Offset defaults to 0, and the animation is back to its original position! Therefore, we need to assign the value of t to offset at the time of the pause so that the value of t can be maintained at the time of the pause. So set offset = pausedTime
namely
-(void)pauseLayer:(CALayer*)layer { CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; Layer. Speed = 0.0; layer.timeOffset = pausedTime; }Copy the code
Restoration of animation
To restore the animation, you need to do two things:
1. Set the speed to 1 and offset to 0 at the same time, otherwise the offset will not be correct for the next pause.
2. The value of t should be equal to the value of the pause above. Because the animation continues from the pause point
T = (tP-begin) *speed + offset
The pause time is still the pausedTime calculated above, so we need to construct the equation so that when speed is equal to 1 and offset is equal to 0, t on the left side of the equation is equal to timeOffset calculated during the pause. namely
t = tp-begin = pausedTime
So begin must equal TP-pausedTime, as follows
-(void)resumeLayer:(CALayer*)layer { CFTimeInterval pausedTime = [layer timeOffset]; Layer. Speed = 1.0; Layer. The timeOffset = 0.0; Layer. BeginTime = 0.0; CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; layer.beginTime = timeSincePause; }Copy the code
So, why set beginTime to 0 first and then set beginTime to TP-PausedTime?
Because convertTime:fromLayer: the beginTime of the layer is used when calculating the time of the current layer,
self.nicoLayer.beginTime = 0;
NSLog(@"beginTime1 = %f", self.nicoLayer.beginTime);
CFTimeInterval tp1 = [self.nicoLayer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog(@"tp1 = %f", tp1);
self.nicoLayer.beginTime = 10;
NSLog(@"beginTime2 = %f", self.nicoLayer.beginTime);
CFTimeInterval tp2 = [self.nicoLayer convertTime:CACurrentMediaTime() fromLayer:nil];
NSLog(@"tp2 = %f", tp2);Copy the code
Print result:
BeginTime1 = 0.000000 TP1 = 81431.807847 beginTime2 = 10.000000 TP2 = 81421.808063Copy the code
Thus, convertTime:fromLayer: method will use the above formula when calculating, so it is necessary to restore beginTime to 0 before calculating, so as to get the correct time.
Simple understanding:
1. When the beginTime is positive, the time point will be moved forward by beginTime seconds when calculating the animation time
2. If timeOffset is positive, the animation time will be moved back by timeOffset seconds
Meng Point understanding:
1. What is CACurrentMediaTime()?
A: CACurrentMediaTime() is the result of converting the CPU tick number of mach_Absolute_time () above to seconds. CACurrentMediaTime() is also not affected by system time, only by device restart and hibernation behavior.
2. What does the layer object call convertTime: fromLayer:? Why do you do that?
A: Calculates the absolute time of the current layer
3. What is timeOffset? Pause animation why do I set this?
T = (tP-begin) * speed + offset, set timeOffset so that when speed is 0, the animation time (or position) is equal to the pause time (or position).
4. What is beginTime? Why set beginTime twice? Set it to 0 the first time and to timeSincePause the second time?
Answer: Same as above, in order to make the equation true with speed set to 1.
5. What is a timeSincePause?
A: PausedTime is fixed, tp increases as time goes by, so timeSincePause is the interval between the pause and the current time, beginTime is set to timeSincePause, Moves the animation forward to the point in time when it was last stopped.
reference
Apple Document: Pausing and Resuming Animations
Stack Overflow: Comprehend pause and resume animation on a layer
MrPeak: time processing by iOS