Search code examples
iosuiviewuiimageviewcgaffinetransformuianimation

Metronome-Like Animation of UIImageView iOS


I'd like to create an animation of an image that makes the image swing back and forth, much like a metronome. The goal of the animation is to simulate a "spinner" like effect. Imagine this with just the top half, the arrow will move back and forth until it stops on it's target. enter image description here

So far I've been able to get the arrow to move back and forth, however the anchor point of the animation is the center of the image. I'd like that anchor to be the bottom center of the image. In order to simulate a metronome, the anchor can't be where it currently is. How do I go about making this change? The animation code is below:

#define RADIANS(degrees) ((degrees * M_PI) / 180.0)


CGAffineTransform leftWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(90));
CGAffineTransform rightWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-90));

arrow.transform = leftWobble;  // starting point

[UIView beginAnimations:@"wobble" context:(__bridge void *)arrow];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:10];
[UIView setAnimationDuration:.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(wobbleEnded:finished:context:)];

arrow.transform = rightWobble; // end here & auto-reverse

[UIView commitAnimations];

Solution

  • CALayer has an anchorPoint property that you can set to the bottom of your arrow image.

    Apple had an iOS sample project called Metronome that does this. I can't seem to find it online anymore so perhaps it has been removed, but here is a code snippet from it:

        // Set up the metronome arm.
        metronomeArm = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"metronomeArm.png"]];
    
        // Move the anchor point to the bottom middle of the metronomeArm bounds, so rotations occur around that point.
        metronomeArm.layer.anchorPoint = CGPointMake(0.5, 1.0);