Twitter recently updated its iOS app with a really cool startup animation that transitions from the Default.png to the timeline view. The animation uses the Twitter bird as a window into the timeline view, and then zooms it in. Here’s a demo:

Replicating the basic animation it is fairly easy. To make the starting point as simple as possible, I used a screenshot of my timeline rather than a full-blown UITableView. The other thing that’s required is the Twitter logo, and Twitter lets you download that from its Brand Assets page.

So let’s dive right in.

I started off with an empty Swift project, and wrote all my code in the AppDelegate itself.

First, let’s add the screenshot on the window:

let imageView = UIImageView(frame: self.window!.frame)
imageView.image = UIImage(named: "twitterscreen")

Our strategy to use the logo as a window into the main view can be implemented as a mask on CALayer. Every CALayer has a mask property that is also a CALayer and lets you mask the main layer or view. Here’s how Apple describes it:

The mask layer’s alpha channel determines how much of the main layer’s content and background shows through. Fully or partially opaque pixels allow the underlying content to show through but fully transparent pixels block that content.

This sounds like what we want. We can put the Twitter logo in a new layer using the contents property, and use this layer as a mask for the image view:

self.mask = CALayer()
self.mask!.contents = UIImage(named: "twitter logo mask").CGImage
self.mask!.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
self.mask!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.mask!.position = CGPoint(x: imageView.frame.size.width/2, y: imageView.frame.size.height/2)
imageView.layer.mask = mask

Try running the project, and you should have the mask setup.

Next up, animating the mask. You’ll observe that the bird reduces in size for a bit, and then increases in size, so to do that using just one animation, let’s use CAKeyframeAnimation.

let keyFrameAnimation = CAKeyframeAnimation(keyPath: "bounds")
keyFrameAnimation.duration = 1
keyFrameAnimation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut), CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)]
let initalBounds = NSValue(CGRect: mask!.bounds)
let secondBounds = NSValue(CGRect: CGRect(x: 0, y: 0, width: 90, height: 90))
let finalBounds = NSValue(CGRect: CGRect(x: 0, y: 0, width: 1500, height: 1500))
keyFrameAnimation.values = [initalBounds, secondBounds, finalBounds]
keyFrameAnimation.keyTimes = [0, 0.3, 1]
self.mask!.addAnimation(keyFrameAnimation, forKey: "bounds")

Getting the animation’s final size and timing right took some hit and trial, but I’d say the final result seems like a decent copy of the original one:

One improvement could be having Twitter’s blue color outside the mask. Pull requests welcome! See the project on GitHub.

Liked the post? Follow me on Twitter @r0unak and try Design Shots, our Dribbble app.