Skip to content

Building animatable buttons with Shapes

DenHeadless edited this page Sep 24, 2014 · 1 revision

We'll use DTShapeButton to build animatable geometric-shape buttons. DTShapeButton is a UIButton, that has a DTShapeView added as a subview. By default, this DTShapeView fills entire button frame. And since any DTShapeView property is animatable, changing it's path can be used to animate changing button geometric shape. But we should be careful here, because changing shape is not so simple as it might sound. Why is that?

AppStore download button

Let's take a look at AppStore download button. It has initial shape of rounded rectangle. After user taps it, it animates to infinitely spinning circle. And you might think, ok, i will simply animate from rounded rectangle to circle, and that's it. But after trying to do so you will learn, that animation is drawing unnecessary angles and animates not exactly how you would expect it to. And after looking at the docs for CAShapeLayer, you will immediately understand why

 If the two paths have a different number of control points or segments the results are undefined. 

Here's how UIBezierPath draws rounded rectangle

And here's how a circle is drawn

When changing paths, CAShapeLayer tries to interpolate between these lines and, obviously, fails, because number of lines and control points differs. One obvious solution would be to draw shapes with the same number of control points. At it's core, circle can be drawn as a rounded square with corner radius of half of it's side. And this approach works fine, but only on iOS simulator, not on the actual device. This happens because of device optimizations, it seems like iOS is automatically estimating how shapes could be drawn. And even if you created shape as a rounded rectangle, if it can be drawn as a simple circle, it will be drawn as a circle with a same number of control points as a circle.

There are different solutions, that can help you here.

Our approach is to have 2 buttons instead of one, one in a form of rounded rectangle, and one in a form of circle. Button animation consists of two parts. First part is to animate to rounded rectangle with a very small width. When first animation is completed, we hide first button, and show the second one. This way transition between two buttons is almost unnoticable.

Starting state

Middle state

Finished state

And here's result in motion:

Having two buttons is not a panacea, though. Sometimes you might go in completely different direction. For example, if you want to build iOS 7 Voice Memos record button, having two buttons is no longer going to cut it.

Voice memos record button

Solution here is a little different. Instead of changing button shape itself, we chose to animate mask of CAShapeLayer. This is possible, because CALayer mask can itself be a CAShapeLayer. To achieve implicit animations with it, DTAnimatableShapeLayer class was created. After that, animating button state change is a matter of simply decreasing or increasing mask around a button.

You can take a look at both buttons implemented in example project in ShapesExample folder.