6

I'm animating a clock arm from pointing towards 12 o'clock to the current time. If it is say, 11 o'clock, I want the arm to rotate clockwise to the 11 o'clock position. But of course if I use:

 CGAffineTransform rotation = CGAffineTransformMakeRotation(2*M_PI*11/12);

 [UIView animateWithDuration:3.0
                  animations:^{
                         clockArm.transform = rotation;
                     }];

the rotation goes counterclockwise. I tried:

CGFloat angle = 2*M_PI*11/12;
CGAffineTransform firstRotation = CGAffineTransformMakeRotation(M_PI-0.001);
CGFloat firstRotationTime = 3.0*(M_PI/angle);
CGAffineTransform secondRotation = CGAffineTransformMakeRotation(angle);
CGFloat secondRotationTime = 3.0 - firstRotationTime;

[UIView animateWithDuration:firstRotationTime
                      delay:0.0
                    options:UIViewAnimationCurveLinear
                 animations:^{
                         self.clockArm1.transform = firstRotation;
                     }
                 completion:^(BOOL finished) {
                     [UIView animateWithDuration:secondRotationTime
                                           delay:0.0
                                         options:UIViewAnimationCurveLinear
                                      animations:^{
                                          self.clockArm1.transform = secondRotation;
                                      }
                                      completion:^(BOOL finished){
                                      }];
                 }];

The animation does go clockwise, but it is choppy - the animation still seems to be doing a UIViewAnimationEaseInOut for the first animation. What am I doing wrong, or is there another way to accomplish what I want?

iDev
  • 23,310
  • 7
  • 60
  • 85
Darren
  • 10,091
  • 18
  • 65
  • 108
  • Have a look at the accepted answer of http://stackoverflow.com/questions/9844925/uiview-infinite-360-degree-rotation-animation. Does that help? – Martin R Oct 10 '12 at 17:59

3 Answers3

7

You can use the completion block of CATransaction to set the rotation property of the view when the animation has finished. The following function worked in my test case:

- (void) rotateViewAnimated:(UIView*)view
               withDuration:(CFTimeInterval)duration
                    byAngle:(CGFloat)angle
{
    [CATransaction begin];
    CABasicAnimation *rotationAnimation;
    rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.byValue = [NSNumber numberWithFloat:angle];
    rotationAnimation.duration = duration;
    rotationAnimation.removedOnCompletion = YES;

    [CATransaction setCompletionBlock:^{
        view.transform = CGAffineTransformRotate(view.transform, angle);
    }];

    [view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
    [CATransaction commit];
}

You use it like

[self rotateViewAnimated:self.clockArm1 withDuration:3.0 byAngle:2*M_PI*11./12.];
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
4

Thanks for @Martin R 's accepted answer. I edited a bit:

I replaced this line

rotationAnimation.removedOnCompletion = YES;

with these two lines:

rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;

Then, in the CompletionBlock, I added one more line:

[view.layer removeAllAnimations];

So, the code finally becomes:

    - (void) rotateViewAnimated:(UIView*)view
           withDuration:(CFTimeInterval)duration
                byAngle:(CGFloat)angle
    {
        [CATransaction begin];
        CABasicAnimation *rotationAnimation;
        rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        rotationAnimation.byValue = [NSNumber numberWithFloat:angle];
        rotationAnimation.duration = duration;
        // changed by me
        rotationAnimation.removedOnCompletion = NO;
        rotationAnimation.fillMode = kCAFillModeForwards;


        [CATransaction setCompletionBlock:^{
        view.transform = CGAffineTransformRotate(view.transform, angle);
        // added by me
        [view.layer removeAllAnimations]; // this is important

        }];

    [view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
    [CATransaction commit];

    }

My references are from:

1, https://stackoverflow.com/a/3586433/2481444

2, After rotating a CALayer using CABasicAnimation the layer jumps back to it's unrotated position

Community
  • 1
  • 1
Yang
  • 404
  • 7
  • 13
1

As mentioned in comments, try this iphone UIImageView rotation or UIView Infinite 360 degree rotation animation?

#import <QuartzCore/QuartzCore.h>

- (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"];
}
Community
  • 1
  • 1
iDev
  • 23,310
  • 7
  • 60
  • 85
  • Thanks, that did work for the animation, but for some reason after the animation completes my clock hand returns to the 12 o'clock position. I've tried looking at the documentation on CABasicAnimation, CAPropertyAnimation and CAAnimation. I tried setting the removedOnCompletion property to NO, but I haven't been able to figure out how to get the transformation to persist. – Darren Oct 11 '12 at 16:02