views:

5523

answers:

5

I'm writing an iPhone app, and I've got an image which I'ld like to have swirl outwards.

Currently my code looks like this (wrapped in a beginAnimations/commitAnimations block):

scale = CGAffineTransformScale(CGAffineTransformIdentity, 5.0f, 5.0f);
swirl = CGAffineTransformRotate(scale, M_PI);
[player setTransform:swirl];    
[player setAlpha:0.0f];

But I find that if I try to change the angle of the rotation to, say, 4*M_PI, it doesn't rotate at all. Is it possible to get a 720˚ rotation using CGAffineTransformRotate, or do I have to switch to another technology?

If I have to switch to another technology, would you recommend using another thread (or a timer) to do the animation myself, or would OpenGL be a better route to go?

Thanks,
Blake.

+5  A: 

You can, but you will need to split your animation into half-circle rotations. I provide an example of this in response to this question, using a repeating CABasicAnimation applied to the layer underneath the view. As I suggest there, doing these half-rotations as parts of a CAKeyframeAnimation would probably be the better way to structure this, because the animation would be smoother (there's a slight hitch in between half-rotations in my example), and you could do a nice acceleration / deceleration at the start and end.

Brad Larson
Cool. This seems to do what I was hoping for, and I figure I can add a scaling transform to it as well, and get a notification back to my class on my own.Thanks!
bwinton
It is incorrect to say that you have to split an animation into half-circle rotations. See my answer.
mahboudz
You do, if you rely on a CATransform3D as the value you're animating. I hadn't thought about your approach, using the keypath extensions that Core Animation provides for the transform structure fields. You're right, that's probably a cleaner way to approach this.
Brad Larson
+2  A: 

Answer to title: Yes, CGAffineTransform can rotate more than 360 degrees just fine.
Answer to question: Yes, but you cannot animate it because there is nothing to animate.

Remember that the affine transformation is a matrix, and that a rotation matrix contains the sine and cosine numbers precomputed. It's as if you said:

CGFloat angle = 4.0 * M_PI;
NSAffineTransformStruct matrix = {
 .m11 = cos(angle),
 .m12 = sin(angle),
 .m21 = -sin(angle),
 .m22 = cos(angle),
 .tx = 0.0,
 .ty = 0.0
};
NSAffineTransform *transform = [NSAffineTransform transform];
[transform setTransformStruct:matrix];

Now, let's review some sample values for cos and sin:

  • cos(0π) = 1
  • sin(0π) = 0
  • cos(2π) = 1
  • sin(2π) = 0
  • cos(4π) = 1
  • sin(4π) = 0

Remember, the matrix doesn't contain the angle -- it only contains the cosine and sine. And those values don't change from one multiple of a circle to another. Therefore, there is nothing to animate.

(Note that Calculator.app gives wrong results for cos(xπ) and sin(xπ). Try Grapher, calc, or Google.)

You will need to split the animation into fractions of a circle. The half-circles Brad Larson suggested will do just fine.

Peter Hosey
You don't have to split the animation. See my answer.
mahboudz
+12  A: 

You can rotate a view, by some number of radians, regardless of whether it is less than a full rotation or many multiples of a full rotation, without having to split the rotation into pieces. As an example, the following code will spin a view, once per second, for a specified number of seconds. You can easily modify it to spin a view by a certain number of rotations, or by some number of radians.

- (void) runSpinAnimationWithDuration:(CGFloat) duration;
{
    CABasicAnimation* rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 /* full rotation*/ * rotations * duration ];
    rotationAnimation.duration = duration;
    rotationAnimation.cumulative = YES;
    rotationAnimation.repeatCount = 1.0; 
    rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];

    [myView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
mahboudz
This works. Thanks for posting it and for insisting when commenting on other people's posts! I was about to split my animation!
Dimitris
I used to split them too and that just didn't seem right.
mahboudz
A: 

a question: how to retain the UIView position once completed the rotation?

Néstor
You should ask this as a separate question, not as an answer on this question.
Peter Hosey
even i've found the answer. thnx anyway
Néstor
A: 

Note, it is stupendously easy to do this as a "real" "do it yourself" animation, just using quartz, so you should perhaps try that.

You don't need to be Hayao Miyasaki to animate this! Here is the entire function to animate it:

-(void) makeMeSpinAround

{

a_global += 3.5;

yourHappyImageView.transform =

CGAffineTransformMakeRotation(degreestoradians(a_global));

}

That's the whole thing.

Just fire that off every frame. (If it is after 1970 when you are reading this, just use CADisplayLink to fire off a timer. If it is before 1970, just use a timer like this...

[NSTimer scheduledTimerWithTimeInterval:0.016667 target:self selector:@selector(makeMeSpinArond) userInfo:nil repeats:YES] )

So, now you can animate it exactly as you want "from scratch."

Joe Blow