in iOS

Duplicating Launchboard Wobble

One of the most requested features for Flower Garden is to be able to move pots around. Not only do players want to group similar flowers or colors together, but they also want to be able to take advantage of the properties of the different gardens. I figured the best thing to do this is by mimicking the wobble on the iPhone launchboard icons when you enter edit mode.I have to say, I’m not a huge fan of the wobble movement, but it does indicate that icons are “loose” and movable. And since most players are already familiar with that metaphor, and the overhead view of Flower Garden resembles the launchboard quite a bit (not coincidence), I might as well go with it.

Garden02

Each pot is a UIButton with a custom image (rendered from OpenGL into an off-screen target). So when I enter edit mode, I need to wobble them like the launchboard. Easier said than done.

I searched online and asked on Twitter, but surprisingly, nobody had done a perfect, reverse-engineering of the wobble movement. Most mentions just do a z rotation on a sine wave, which is not that close of the launchboard animation.

I found that to get closer to the real thing, you need to do that rotation around a random point offset from the center. That’s better, but it’s still not perfect. There’s an additional extra twitch in the original Apple animation. I’d love it if someone already reverse-engineered it and has a better model for that animation.

In the meanwhile, I figured I would share the core I’m using. It’s all very straightforward, except for the part about rotating a UIView around a different point other than the center. I initially thought I could just concatenate transforms, but no matter how you do it, UIKit always interprets the transform around the center.

To be more precise, it interprets the rotation around the anchor point, and that’s what you need to modify. The anchor point is defined in units that are a percentage of the layer’s size, so by default it’s 0.5, 0.5. When you move the anchor point, the layer itself will move, so you need to fix it up by moving the layer’s position by the same amount in the opposite direction.

This is what the final code looks like:

void StartPotWobble(UIButton* button, Random& random)
{
	const Range posOffset(0.10f, 0.2f);
	const float amplitude = random.GetFloat(1,1.5f);
	const Vec2 startCenter(Sign(random.GetFloat(-1,1))*random.GetFloat(posOffset), Sign(random.GetFloat(-1,1))*random.GetFloat(posOffset));
	const Vec2 endCenter(Sign(random.GetFloat(-1,1))*random.GetFloat(posOffset), Sign(random.GetFloat(-1,1))*random.GetFloat(posOffset));

	CGPoint center = button.center;	
	button.layer.anchorPoint = CGPointMake(0.5f + startCenter.x, 0.5f + startCenter.y);
	CGRect bounds = button.bounds;
	button.layer.position = CGPointMake(button.layer.position.x + bounds.size.width*startCenter.x, 
									button.layer.position.y + bounds.size.height*startCenter.y);
	button.transform = CGAffineTransformMakeRotation(-amplitude*DegToRad);
		
	[UIView beginAnimations:nil context:NULL];
	[UIView setAnimationRepeatAutoreverses:YES];
	[UIView setAnimationRepeatCount:FLT_MAX];
	[UIView setAnimationDuration:0.12];
	[UIView setAnimationDelay:random.GetFloat(0,0.09f)];
		button.transform = CGAffineTransformMakeRotation(+amplitude*DegToRad);
	[UIView commitAnimations];
}
	
	
void StopPotWobble(UIButton* button)
{
	[button cancelAllAnimationsRecursively];
	button.transform = CGAffineTransformIdentity;
	CGPoint anchor = button.layer.anchorPoint;
	CGPoint offset = CGPointMake((0.5f - anchor.x)*button.width, (0.5f - anchor.y)*button.height);
	button.layer.anchorPoint = CGPointMake(0.5f, 0.5f);
	button.layer.position = CGPointMake(button.layer.position.x + offset.x, button.layer.position.y + offset.y);
}

Ah yes, and I couldn’t find a way to loop the animation infinitely (without using the new UIView animation syntax), so FLT_MAX will have to do 🙂 Any cleaner way?

If someone has an animation that more closely resembles the iPhone launchboard, I’d love to hear about it. Let me know and I’ll update this post.

  • Great post! I’ll keep it saved for future reference.

  • sid

    This post got me into thinking mode, I used to think the wobble animation was just a simple z axis rotation like the facebook app has done, but looking closely, its not, and definitely it looks like swinging around a random anchor point. I would sure love to try your algorithm.
    BTW, I remember using a negative number used to loop infinitely for UIView animation, don’t know what happened to that, or was it just a deja vu 🙂

  • Adam

    Your post made me curious, so I fired up iOS Simulator and zoomed in on the app wobble animation. I think I figured out how it works.
    It seems to me that the app wobble is a combination of two animations: a rotation and a translation. The icon rotates around its center point, using a triangle wave (i.e., it transitions linearly between the two extreme rotations.)
    The icon also has a small translation animation — its center point moves back and forth linearly between two randomly offset points.
    These two animations have the same frequency, but they are offset from each other by a random amount of time. In fact, when you look at a wobbling home screen, you can see that each icon has a slightly different wobble. When the animations are “aligned,” the icon appears to be rotating around a random point. When they aren’t aligned, the icon looks a lot more jittery.
    I hope that helps, and that it made sense. Good luck with Flower Garden!

  • ajv

    UIImageView Class Reference

    @property(nonatomic) NSInteger animationRepeatCount

    Discussion

    The default value is 0, which specifies to repeat the animation indefinitely.