Using CABasicAnimation to rotate a UIImageView more than once

I use CABasicAnimation to rotate a UIImageView 90 degrees clockwise, but I need it to rotate 90 degrees later from its position after the initial rotation 90 degrees.

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.duration = 10; animation.additive = YES; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.fromValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(0)]; animation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(90)]; [_myview.layer addAnimation:animation forKey:@"90rotation"]; 

Using the above code works initially, the image remains at an angle of 90 degrees. If I call it again so that it rotates another 90 degrees, the animation will begin by returning to 0 and turning 90 degrees, not 90 ... 180 degrees.

I got the impression that animation.additive = YES; will cause further animations to use the current state as a starting point.

Any ideas?

+11
ios core-animation
Jul 02 '13 at 13:23
source share
2 answers

pass incremental angle value see my code

 static int imgAngle=0; - (void)doAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.duration = 5; animation.additive = YES; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.fromValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(imgAngle)]; animation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(imgAngle+90)]; [self.imgView.layer addAnimation:animation forKey:@"90rotation"]; imgAngle+=90; if (imgAngle>360) { imgAngle = 0; } } 

The above code is for ideas only. Its not verified

+7
Jul 02 '13 at 13:47 on
source share
— -

tl; dr: It is very easy to abuse removeOnCompletion = NO , and most people do not understand the consequences of this. The correct decision is to change the value of the model "for real".




First of all: I am not trying to judge or be evil to you. I see the same misunderstanding over and over again, and I understand why this is happening. Explaining why everything happens, I hope that everyone who experiences the same problems and sees this answer will learn more about what their code does.

Something went wrong

I got the impression that animation.additive = YES; will cause further animations to use the current state as a starting point.

This is very true, and this is exactly what is happening. In this sense, computers are ridiculous. They are always exactly what you tell them, and not what you want to do from them.

removeOnCompletion = NO can be a bitch

In your case, the villain is a line of code:

 animation.removedOnCompletion = NO; 

It is often not recommended to use the final animation value after the animation is completed. The only problem is that this happens without removing the animation from the view. Animations in Core Animation do not change the underlying property that they animate, they simply animate it on the screen. If you look at the actual value during the animation, you will never see how this changes. Instead, the animation works on what is called the presentation layer.

Usually, when an animation finishes, it is removed from the layer, and the presentation layer disappears and the model layer reappears on the screen. However, when you save the animation attached to the layer, everything looks as it should on the screen, but you made the difference between what the property says is the transformation and how the layer rotates on the screen.

When you set up the animation as additive, it means that the from and to values ​​are added to the existing value, as you said. The problem is that the value of this property is 0. You never change it, you just animate it. The next time you try to add this animation to the same layer, the value will still not be changed, but the animation does exactly what it has configured to execute: "animate additively from the current model value."

Decision

Skip this line of code. As a result, however, the rotation does not stick. The best way to do this is to change the model. Set a new final rotation value before the rotation animation so that the model looks as it should when the animation is deleted.

byValue is like magic

CABasicAnimation has a very convenient property (which I will use) called byValue , which can be used to create relative animations. It can be combined with toValue and fromValue to make many different kinds of animations. Various combinations are indicated in its documentation (in the section). The combination I'm going to use:

byValue and toValue are not nil . Interpolates between (toValue - byValue) and toValue .

Some actual code

With an explicit toValue of 0, the animation goes from "currentValue-byValue" to "current value". By changing the model, the first current value is the final value.

 NSString *zRotationKeyPath = @"transform.rotation.z"; // The killer of typos // Change the model to the new "end value" (key path can work like this but properties don't) CGFloat currentAngle = [[_myview.layer valueForKeyPath:zRotationKeyPath] floatValue]; CGFloat angleToAdd = M_PI_2; // 90 deg = pi/2 [_myview.layer setValue:@(currentAngle+angleToAdd) forKeyPath:zRotationKeyPath]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:zRotationKeyPath]; animation.duration = 10; // @( ) is fancy NSNumber literal syntax ... animation.toValue = @(0.0); // model value was already changed. End at that value animation.byValue = @(angleToAdd); // start from - this value (it toValue - byValue (see above)) // Add the animation. Once it completed it will be removed and you will see the value // of the model layer which happens to be the same value as the animation stopped at. [_myview.layer addAnimation:animation forKey:@"90rotation"]; 

A small caveat :

I did not run this code, but I am pretty sure that it works as it should, and that I did not make any typos. Correct me if I do this. All discussion remains valid.

+63
Jul 02 '13 at 20:17
source share



All Articles