How to build a nice Hamburger Button transition in Swift
Hamburger buttons may have become somewhat of a cliché in interface design lately, but when I came across a particularly nice transition of a hamburger button on dribbble, I had to try and recreate it in code.
Here's the original shot by the CreativeDash team:
You'll notice how the top and bottom strokes of the hamburger form a X, while
the middle one morphs into an outline. I knew this effect could be recreated
using CAShapeLayer
, but first I had to create a CGPath
for each of the three
strokes.
The short, straight strokes can be figured out manually:
For the middle stroke however, I created a path in Sketch that starts from the middle and seamlessly transitions into the outline.
I then exported this path as an SVG file and imported it into the old
PaintCode 1, which converted the file into a code snippet that created a
UIBezierPath
. I then rewrote said piece of code into the following
instructions that create the desired CGPath
object:
There probably is a library that allows you to load CGPath
s straight from SVG
files, but for a short path like this one, having it in code is not a big deal.
In my UIButton
subclass, I added three CAShapeLayer
properties and set the
their paths accordingly:
Then I styled all three of them like so:
In order to calculate their bounds correctly, I needed take the size of the
stroke into account. Thankfully, CGPathCreateCopyByStrokingPath
will create a
path that follows the outlines of the original stroke, therefore its bounding
box will then fully contain the contents of the CAShapeLayer
:
Since the outer strokes will later rotate around their right-most point, I had
to set their anchorPoint
accordingly when layouting their layers:
Now, when the button changes state, it should animate the three strokes to their new positions. Once again, the two outer strokes are easy. For the top stroke, I first moved it inward by 4 points to keep in centered, then rotated it by negative 45 degrees to form one half of the X:
(The bottom stroke is left as an exercise to the reader.)
Again, the middle stroke is a little trickier. In order to achieve the desired
effect, I needed to animate the CAShapeLayer
's strokeStart
and strokeEnd
properties separately.
First I had to figure out the correct values for the two properties in the two states. Note that the even in its hamburger state, the stroke does not start at 0. By having the path extend slightly beyond the left edge of the outer strokes, we can achieve a nice anticipation effect when applying the timing function later:
Now we only need to create the animations and add them to the layer:
Putting everything together, the result looks something like this:
I'm pretty happy with how it turned out. If you are too, you can find the code on GitHub and follow me on Twitter.
Posted in working-on