CABasicAnimation starts at the current layer position

This is my second week of Obj-C programming, and I am experiencing a slight animation problem.

I am using this animation:

CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)]; fullRotation.duration = 4; fullRotation.repeatCount= 1000; [[stick layer] addAnimation:fullRotation forKey:@"60"];} 

This animation starts by launching my application, then I press buttons that change the duration of the animation when I click, but new animations (which have the same code but with different durations) start from the original “stick” image position. What can I do to make other animations start from the current position of the stick, which makes 360-degree turns? Thanks.

Part of the code for a more detailed explanation:

 -(void)viewDidAppear:(BOOL)animated{ CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)]; fullRotation.duration = 4; fullRotation.repeatCount= 1000; [[stick layer] addAnimation:fullRotation forKey:@"60"];} - (IBAction)button1:(UIButton *)sender { CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)]; fullRotation.duration = 6; fullRotation.repeatCount= 1000; [[stick layer] addAnimation:fullRotation forKey:@"60"];} 
+6
source share
1 answer

There are several different ways to achieve the result you need, but before looking at them, I must explain the root cause of the problem.

What's happening

What you see on the screen during the animation does not necessarily correspond to the property values ​​of these layers. In fact, adding animations to a layer does not change the animated property of the layer. The animation and values ​​that you see on the screen happen on a rendering server that runs in a different process than your application. You cannot get to these exact values, but you can get closer to the name of the values ​​of the view. Since we cannot get to the values ​​of the rendering server, we often talk only about the model values ​​(the actual values ​​of your layer object) and the values ​​of the view (which appears on the screen (or at least very close to it)).

Only specifying toValue for CABasicAnimation means that it is animated from the current model value and the specified value. Please note that the documentation states that this is the current value of the view, but that is actually incorrect . Since the value of the model never changes, this means that when a second animation is added, it animates from the unchanged untreated value of the model to toValue .

(As a note: since two animations use the same key, the new one replaces the old one. This does not really matter, since the animations are not additive, so even if the animation has not been replaced, the new animation will write its value on top of the old animation value).

Various ways of fixing it

There are many ways to get the right behavior, starting with the simplest version.

Adding an explicit fromValue

As mentioned above, only with non-nil toValue animation start at the current model value. However, you can add non-nil fromValue , which is the current value of the view.

You can get presentation from presentationLayer level. From it you can get the current rotation angle using the same path that you are animating and a little KVC (key coding):

 NSNumber *currentAngle = [switch.layer.presentationLayer valueForKeyPath:@"transform.rotation"]; 

If you only established that as fromValue animation you would get the correct initial value, but the rotation might not be 360 ​​degrees more. While you can define the angle and create a new toValue , there is another byValue property that makes a relative change. Specifying byValue and fromValue (but not toValue) means:

Interpolates between fromValue and (fromValue + byValue) .

So you can change the animation code to:

 CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; fullRotation.fromValue = currentAngle; // the value read from the presentation layer fullRotation.byValue = @(2.0*M_PI); fullRotation.duration = 6.0; 

At this point, your code should continue from the correct value, but the overall animation speed might look a little strange (it should have looked odd even before this code change). For a view that rotates continuously, you probably want it to always rotate at the same speed (as opposed to accelerating and decelerating for each rotation). You can get this by setting the animation to a linear synchronization function:

 fullRotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; 

This should give you the behavior you need. Some stylistic notes are to specify either INFINITY or HUGE_VALF for counting repetitions, if you don't want it to rotate exactly 1000 times, and use a more descriptive key when adding a layer (unless you use the key for something else ( e.g. removing animation from a layer):

 fullRotation.repeatCount = INFINITY; [stick.layer addAnimation:fullRotation forKey:@"rotate continuously"]; 

Layer speed change

I would think twice before using this as a solution in production code, but it can be interesting as a training exercise. Since what you mostly do slows down the animation, changing the duration from 4 seconds to 6 seconds, one thing you can do to create the same effect is to slow down the level. Note that this will have side effects (all animations on this layer and all of its sublayers will also be affected).

You cannot modify the animation after adding it to the layer, but the layer itself also conforms to the CAMediaTiming protocol, which means that it has the speed property. Setting the speed to 4.0/6.0 and saving the old animation on the layer will slow it down, making each rotation in 6 seconds instead of 4.

Once again, a bit hacked but fun as a training exercise.

+15
source

Source: https://habr.com/ru/post/973371/


All Articles