Note: This post is designed for someone who has a beginning to intermediate knowledge of iOS.
At Intrepid, we are lucky to have designers with a talent for animation, and our apps look best when their imaginations run wild. Executing their ideas requires a development team with the skills to keep up. Thus, the Intrepid Animation Club was born! Our mission: to seek out slick animations, build them, share them, and compare notes. Today I’ll be sharing the first of our adventures and a few key takeaways:
Social Tunes Sharer
We selected a design of our own designer, Aaron Tenbuuren, who recently posted a Dribbble shot of some fancy UI for sharing content via social media:
Though originally designed for desktop interaction, we decided to take on the challenge of adapting it for mobile. Notice that the animation has three key points:
- The “slide in” animation that reveals the individual social buttons, after the user taps “SHARE”
- The “ripple” effect that expands outward when the user taps a social media button
- The “slide out” animation that happens after the ripple– notice the subtle difference in the way buttons move in comparison to step 1
We came up with a couple of different solutions, and the code for each developer’s submission is available on the animation club GitHub page. If you’re an Android dev, check out Anton’s example. Let’s take a look at the general approach from an iOS perspective.
I decided to lay out my view hierarchy directly in Interface Builder, because it’s easier for me to visualize the layout and make changes. Most of the other implementations opted for setting up views in code using a custom UIView subclass (more on that later), but the resulting hierarchies are similar.
The idea is to have two subviews, “Top” and “Bottom”, which contain the buttons. Moving or transforming these containers will let us implement the “slide” animations.
The Slide Animations
Like the majority of animations in iOS, the easiest way to get the sliding effect is to call
animateWithDuration and move the “Top” and “Bottom” container views inside the
But what’s the best way to move the views? Let’s look at two ways that Intrepid devs (Paul and myself) did it:
1. Change the actual layout of the views; since we are using Autolayout, this means altering some constraints.
2. Keep the layout as is, and instead modify the view’s transform property.
I opted for the first method, but after talking with the other devs I decided the second is better because I don’t need to set up outlets from the storyboard to expose the constraints. It also provides a clean separation between the default layout of the UI, defined by constraints, and the transformed state after animation. Returning to the base layout is easy because all we need to do is set the transforms back to the identity.
Let’s take a look at what we have so far. In the simulator, we can slow the animation down by selecting “Debug > Slow Animations”.
Note that the top-level “Social Sharer View” has `clipsToBounds` enabled. That way the “Top” and “Bottom” views are clipped to fit inside its bounds. To visualize how this works, we can turn off `clipsToBounds` and add a border to “Social Sharer View”. The result looks like this:
Rounding the Corners
Rounding corners in iOS is easy thanks to `CALayer` objects. Just set the
cornerRadius on a view’s
layer property. In our case we want both the overall “Social Sharer View” and the “Top” view to have rounded corners with the same radius:
The Ripple Animation
The flashiest piece of the interaction is the subtle highlight that “ripples” outward from the social media buttons when tapped. For Android developers, this type of animation fits with the Material Design Specification and can be achieved using the built-in RippleDrawable. iOS devs have to work a little harder for this effect (much to the amusement of our Android compatriots), but it’s actually pretty easy to achieve.
Let’s take a look at my
RippleAnimation class, which allows me to play a ripple on any view I want by calling the
applyToView function. It works by adding a transparent subview to provide the colored tint. We give it a starting position, and then scale up the transform inside an animation so that the tint expands and fills the superview.
Now I just need to create a
RippleAnimation and apply it to our “Bottom” view, using the tapped button as a starting point.
Once again we can turn off `clipsToBounds` on “Social Sharer View” to better see what’s happening:
In my solution, the legwork is done by code living directly in the view controller, with layout defined by the storyboard. But what if I decided to use this Social Sharer widget in a real app, where it might appear in multiple places? Or if I wanted to share it with the world as an easy-to-use library?
This is where architecture starts to matter, and in that regard I think the other devs did a better job going the extra mile. Let’s take a look at Paul’s.
For starters, he uses a custom
FancyButton, to encapsulate the widget’s implementation. Rather than tie his view’s internal layout to a storyboard or nib, he decides to set up constraints programmatically. This is more work and harder to visualize, but gains the advantage of adapting to an arbitrary number of buttons at runtime. To help manage the constraint setup, he uses Cartography, a neat library that provides an alternative to Autolayout’s visual format strings.
Finally he sets up another subclass to represent the submenu buttons, and exposes a simple public interface for adding them to the widget. All that’s left is to drop a
FancyButton into the storyboard and configure it with a few lines from the view controller.
We’ve seen that this animation is actually pretty simple when you break it down. I think it illustrates some key points that are helpful to keep in mind when creating most animations:
- Figure out a logical way to organize the components into a set of subviews. Remember that Interface Builder is your friend, especially for quick prototyping.
- Set up the basic layout using Autolayout constraints. This helps your view adapt to varying screen sizes.
- Break the animation down into steps, like we did here with “slide in,” “slide out,” and “ripple”.
- When scaling or translating views, consider
CGAffineTransform. It helps separate the basic layout from the animation steps, and it’s often easier than modifying constraints.
- After you’ve got things working, don’t forget to refactor! A little extra effort can go a long way towards making your code reusable.
I find that I can build a wide variety of animations just by following these steps. Mastering them takes time and practice. We look forward to sharing future Animation Club challenges, so stay tuned for more posts.