-
Notifications
You must be signed in to change notification settings - Fork 48
Building animatable buttons with Shapes
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?
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.
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.
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.