10

This should be simple, but I'm having trouble rotating a UIImageView a full 360 degrees, repeated forever.

[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveLinear | UIViewAnimationOptionBeginFromCurrentState animations:^{
    self.reloadButton.imageView.transform = CGAffineTransformRotate(self.reloadButton.imageView.transform, -M_PI);
} completion:^(BOOL finished) {

}];

According to the docs, the signedness of the angle I pass to CGAffineTransformRotate determines the direction of the rotation, but the above code rotates counterclockwise. Same with M_PI.

The angle, in radians, by which this matrix rotates the coordinate system axes. In iOS, a positive value specifies counterclockwise rotation and a negative value specifies clockwise rotation. In Mac OS X, a positive value specifies clockwise rotation and a negative value specifies counterclockwise rotation.

Morrowless
  • 6,856
  • 11
  • 51
  • 81
  • What happens if you do the rotation on the imageViews layer with a CATransform3D? – David Rönnqvist May 08 '12 at 07:07
  • did you try rotating by other angles (say 180 degrees) ? – giorashc May 08 '12 at 07:07
  • @David Thanks for the suggestion but this produced the same results: self.reloadButton.imageView.layer.transform = CATransform3DMakeRotation(-M_PI, 0, 0, 1); – Morrowless May 08 '12 at 07:13
  • Since M_PI is 180 degrees it should be the same. what happens if you rotate my M_PI/4 or -M_PI/4? You should see some difference? – Mark May 08 '12 at 17:08

4 Answers4

14

Christoph is already going the correct way, but there is a far better way to keep it spinning without reinvoke it in the animation delegates every time it ends. This is simply wrong.

Just set the repeatCount property of your animation to HUGE_VALF.

CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.fromValue = @0.0f;
animation.toValue = @(2*M_PI);
animation.duration = 0.5f;             // this might be too fast
animation.repeatCount = HUGE_VALF;     // HUGE_VALF is defined in math.h so import it
[self.reloadButton.imageView.layer addAnimation:animation forKey:@"rotation"];

As stated in the documentation, this will cause the animation to repeat forever.

yinkou
  • 5,756
  • 2
  • 24
  • 40
  • 1
    Ok, did not really think about it, because 270 degrees were enough for me. Setting the repeat count is way better, I will change accordingly. – Christoph May 08 '12 at 18:51
  • Just use `CGFLOAT_MAX` instead of importing something. – Ben Lachman Mar 16 '15 at 04:07
  • Or you directly write `1e50f` since it's all the same... Or we do it as the documentation states. – yinkou Mar 16 '15 at 12:16
  • 2
    @BenLachman actually the docs tell you specifically to use HUGE_VALF. https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CAMediaTiming_protocol/index.html#//apple_ref/occ/intfp/CAMediaTiming/repeatCount – MikeyWard Mar 27 '15 at 19:00
  • @MikeyWard Good point, I didn't notice that in the docs. Any thoughts on why they specify that? It's the only time `HUGE_VALF` is referenced in all of Apple's docs (outside of strto* man pages). Seems a little odd. Maybe a legacy reference? – Ben Lachman Jun 02 '15 at 15:13
  • @yinkou Thanks for the snark, alway appreciated. :-) `HUGE_VALF` seems like it might be legacy. See this question about using it in Swift: http://stackoverflow.com/q/24343908/159801 I think I'll file a documentation bug about it. – Ben Lachman Jun 02 '15 at 15:19
1

Sorry missed first part, view rotates just fine using smaller angle:

[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveLinear | UIViewAnimationOptionBeginFromCurrentState animations:^{
        redView.transform = CGAffineTransformRotate(redView.transform, M_PI_2);
    } completion:^(BOOL finished) {

    }];

I couldn't find any meaningful explanations why it fails with M_PI.

ksh
  • 1,894
  • 19
  • 20
1

I encountered the same issue a while ago. I do not remember the reason for that issue, but this is my solution:

/**
 * Incrementally rotated the arrow view by a given angle.
 *
 * @param degrees Angle in degrees.
 * @param duration Duration of the rotation animation.
 */
- (void)rotateArrowViewByAngle:(CGFloat)degrees
                  withDuration:(NSTimeInterval)duration {

    CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    spinAnimation.fromValue = [NSNumber numberWithFloat:self.currentAngle / 180.0 * M_PI];
    spinAnimation.toValue = [NSNumber numberWithFloat:degrees / 180.0 * M_PI];
    spinAnimation.duration = duration;
    spinAnimation.cumulative = YES;
    spinAnimation.additive = YES;
    spinAnimation.removedOnCompletion = NO;
    spinAnimation.delegate = self;
    spinAnimation.fillMode = kCAFillModeForwards;

    [self.arrowView.layer addAnimation:spinAnimation forKey:@"spinAnimation"];

    self.currentAngle = degrees;
}

and then you can use the delegate methods

- (void)animationDidStart:(CAAnimation *)theAnimation
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag

to rotate keep it rotating. Also, degree and duration parameters can be really high numbers... if this is enough.

UPDATE:
As stated by yinkou,

spinAnimation.repeatCount = HUGE_VALF;     // HUGE_VALF is defined in math.h so import it 

is way better that restarting the animation in the delegate.

PLEASE NOTE:
self.currentAngle is a property remembering the current final rotation.
I needed that to make the view rotate left and right way around.

Christoph
  • 1,525
  • 1
  • 16
  • 27
1

I wrote UIImageView category for this: https://gist.github.com/alexhajdu/5658543.

Just use:

[_myImageView rotate360WithDuration:1.0 repeatCount:0]; //0 for infinite loop 
alexhajdu
  • 410
  • 5
  • 13