Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotating UIView with CGAffineTransform - how to make it go full path and not short one?

I'm making a speedometer app and I want arrow to behave the correct way - if I launch from zero to 200 km in one second, I want it to go around the bar, but if I set my rotation angle big enough, it goes the short way, from the bottom.
How do I make it go almost full circle and not via short path?

Here is the (trivial) code I use for rotation:

[UIView animateWithDuration:0.3 animations:^(){

        self.arrow.transform = CGAffineTransformRotate(CGAffineTransformIdentity, -4.4);

    }];

I figure I can rotate it in small chunks, but sometimes it may be needed to rotate it fast from zero to maximum (for example if we had no reading on speed and got it already at high speed so we need to rotate the arrow most of the screen).

As a side question - how do I queue animations, so that I can apply them one by one?

like image 605
Dvole Avatar asked Jul 29 '13 15:07

Dvole


2 Answers

I made a helper function for this a little while back which is included below. Basically, UIKit is useless for this, you'll have to import the QuartzCore framework and use either CAKeyframeAnimation as @Paul Cezanne pointed out, or CABasicAnimation, which I think will handle this just fine.

Usage is pretty simple, just call the method as detailed below passing the image view that you want to rotate, the rotation you want in radians, and the animation duration and your image view will animate properly. It will always take the "long way" because there is actually a direction specified here.

If you pass positive values to the animation, it will rotate clockwise and then naturally, negative values will cause counter-clockwise rotation.

[self rotateView:imageView rotationInRadians:M_PI duration:2.0];


- (void)rotateView:(UIImageView *)imageView rotationInRadians:(CGFloat)radians duration:(CFTimeInterval)duration
{
    CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];

    [rotationAnimation setToValue:[NSNumber numberWithFloat:radians]];
    [rotationAnimation setDuration:duration];
    [rotationAnimation setCumulative:YES];
    [rotationAnimation setFillMode:kCAFillModeForwards];
    [rotationAnimation setRemovedOnCompletion:NO];

    [imageView.layer addAnimation:rotationAnimation forKey:@"gaugeRotationAnimation"];
}
like image 130
Mick MacCallum Avatar answered Nov 15 '22 04:11

Mick MacCallum


I had a similar problem awhile back. I only needed to go 180 degrees, but sometimes I needed it clockwise, sometimes counter clockwise, but I always flipped back and forth between the two of them, hence the "rotated" property in the code.

You need to use CALayer and CAKeyframeAnimations, and here's the code that worked for me:

-(void) showHideSpinnerWithDuration:(float) duration;
{
    CALayer* layer = _spinner.layer;
    CAKeyframeAnimation* animation;
    animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];

    animation.duration = duration;
    animation.cumulative = YES;
    animation.repeatCount = 1;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;

    if (_rotated) {
        animation.values = [NSArray arrayWithObjects:
                            [NSNumber numberWithFloat:DegreesToRadians(180)],
                            [NSNumber numberWithFloat:DegreesToRadians(0)],
                            nil];
        self.rotated = NO;
    } else {
        animation.values = [NSArray arrayWithObjects:
                            [NSNumber numberWithFloat:DegreesToRadians(0)],
                            [NSNumber numberWithFloat:DegreesToRadians(180)],
                            nil];
        self.rotated = YES;
    }

    animation.keyTimes = [NSArray arrayWithObjects:
                          [NSNumber numberWithFloat:0.0],
                          [NSNumber numberWithFloat:duration], nil];

    animation.timingFunctions = [NSArray arrayWithObjects:
                                 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                 [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], nil];

    [layer addAnimation:animation forKey:@"transform.rotation.z"];
}
like image 34
Paul Cezanne Avatar answered Nov 15 '22 04:11

Paul Cezanne