Using Framer to Prototype iOS Animations

Lea Marolt Sonnenschein

Framer-featureStatic prototypes suck. With static prototypes, you can visualize the app’s visual design, but not the interaction design.

Considering how important interaction design is to apps, static prototypes are like a puzzle with pieces missing.

So why doesn’t everyone create interactive prototypes instead? Well, it can take a lot of time to prototype user interactions using a tool like After Effects. It’s hard to spend so much time when you might throw it away the next day.

Enter Framer: an easy-to-use tool for developers and designers to create interactive prototypes. Framer makes it quick to prototype interactions, iterate on the fly, and bring back the magic!

In this Framer tutorial, you’ll recreate this lovely navigation header animation created by Voleg:


In this tutorial, you will focus on prototyping the animation for the menu expanding/collapsing, as that’s the most interesting part. Let’s get started!

Getting Started

First, download and install the following software (you can use the free trials for the purposes of this tutorial):

Open Framer, and you’ll see the following welcome screen:


Click the Animate example project to get a feel for the IDE:


On the left is the Coding Panel; on the right is the Prototype Panel. The Layers Panel sits in the middle.

Feel free to look through the code to get a preview of what’s coming, but don’t worry if you don’t understand it for now. Close this example project, you’re going to create a new one.

Creating a New Prototype

Create a new file in Framer, by going to File\New. Then, click the Insert\Layer to create a new Layer.


Click in a blank spot in the code editor to unselect the layer attributes. You should then see your new layer in each panel:

  • As code in the Coding Panel
  • As a reference in the Layers Panel
  • As a grey square in the Prototype Panel

Mouse over the name of the layer in the Layers Panel to see its location on the prototype.


Change the name to square in the coding panel.


Click on the square next to the left of the line of code to see and modify the layer’s attributes in the Layers Panel and to move it around in the Prototype Panel.


Drag the square to the middle of the prototype, and observe the changes in the Coding and Layers Panels. It should now look something like this:


The changes you make on the layer by interacting with it on the prototype are immediately reflected in the code — and vice versa.

Being able to use either the code or the visual editor to make our changes is a huge advantage of prototyping with Framer as opposed to working with Xcode and Swift.

Delete the existing code, paste the following in the coding panel, and observe the immediate change in the prototype:

square = new Layer
	x: 250
	y: 542
	height: 250
	width: 250
	backgroundColor: "rgba(255,25,31,0.8)"

Pretty neat, right?

Note: You write code in Framer using CoffeeScript, a simple language that compiles into Javascript. If you’ve never used it before, don’t worry – it’s pretty simple and you can learn a lot about its syntax just by following along with this tutorial.

Note that indentation matters in CoffeeScript, so make sure your indentation matches mine or the code won’t work! Indentation is CoffeeScript’s replacement for {}‘s.

Tabs vs spaces matter too. Framer defaults to use tabs by default, so if you see code that uses spaces like this:


Backspace until you hit the left edge, and replace spaces with tabs to get something like this:


When you’re copying and pasting code and moving to a newline, always backspace to the beginning of the line. Otherwise your code might be interpreted as part of something else.

Your First Framer Animation

Time to make some magic happen! You’ll make a red square turn into an orange circle when it’s tapped.

Add an empty line after your layer definition, then add the following:

square.onTap ->
	square.backgroundColor = "rgba(255,120,0,0.8)"
	square.borderRadius = 150

This makes the shape respond to tap events.

Click on the red square, and it will turn into an orange circle.


The biggest advantage of prototyping with Framer, rather than Xcode and Swift, is the ability to interact with your prototype immediately after you make your change. Removing the time-suck of constantly building and running Xcode projects greatly improves your prototyping speed — and your ability to iterate quickly through your designs.

All right, I know what you’re thinking. Snoozeville! The transition is too sudden, and the user can’t return to the red square. That’s easy to fix.

Instead of specifying what happens to the square layer on a tap, create a new state.

Replace the code you just added with the following:

# 1	
		backgroundColor: "rgba(255,120,0,0.8)"
		borderRadius: 150
# 2
square.onTap ->

Let’s review this section by section:

  1. This defines a new state orangeCircle for square. This new state sets the layer’s backgroundColor to orange and its borderRadius to 150. You can see a full list of properties you can set on layer’s in the framer.js documentation.
  2. This configures the tap event so that square will transition to its next state.

Click the square to see how the transition has improved:


Your shape now animates to the orangeCircle state and animates back to the square’s original state. That’s because you’ve added states to a layer loop, so the state after orangeCircle is the default state.

Let’s try adding another state. Add this line right below section 1:

		backgroundColor: ("green") 
		borderRadius: 50

Now, tap the square in the prototype panel and you’ll see it loop between all three states:


With just a few lines of code, and barely any setup, you’ve created a slick animation!

Think you’re ready for something harder and way cooler, like an actual UI?


Using Framer to Recreate an Animation

Take another look at the animation you’ll be recreating:


Again, you’ll be focusing on the menu expanding/collapsing portion of this animation in this tutorial.

The most important part in recreating an animation is to break it down to its basic components. Not only does this help you understand the animation better, but it also helps you create the step-by-step instructions on how to do it yourself!

There are three different problems to tackle in this animation: The selected state, the deselected state, and the transition between them.



This is what the user sees initially:

  • There are four tappable banners.
  • Each banner has a different color, icon, and title.
  • Each banner casts a shadow on the banner below it.



This is what the user sees when they tap a banner:

  • The top bar with the main color, title and a smaller icon
  • A white background
  • The top bar casts a shadow on the layer underneath
  • Four cards underneath, two on the top, two on the bottom, and spaced evenly
  • The color of the cards depends on the selected banner

The user taps to transition between the two states. In the deselected state, the user taps on one of the colored banners. In the selected state, the user taps on the top bar.

Transitioning from Deselected to Selected


Here’s what happens as the UI transitions from deselected to the selected state:

  • All four banners shrink to slightly less than half of their original height.
  • The tapped banner goes on top, the others fold behind it.
  • The banner animation starts slow, speeds up quite a bit, then slows down dramatically.
  • The only remaining shadow is the one on the top banner.
  • The icon on the quite suddenly shrinks from 100% of its size to nothing. The animation starts quickly but finishes with a lot of damping.
  • The text in the middle of the tapped banner moves vertically to the middle of the top bar.
  • The background cards have no real animation, but the colors of the cards match the tapped banner.

Transitioning from Selected to Deselected


Here’s what happens as the UI transitions from the selected to the deselected state:

  • All four banners expand and move down, so that one begins where the other ends to cover the whole screen. Each banner is 1/4th of the height of the screen
  • The banners are always in a specific order, so the banner you tap on doesn’t influence how the banners appear in the deselected state.
  • The expanding banner animation starts off a little slow, speeds up quite significantly, then finishes with a lot of damping.
  • Each banner casts its own shadow.
  • The icon reappears; it grows from nothing to 100% of its size. The animation starts quickly, then finishes with damping.
  • The text moves about 4/5ths of the way down the expanded banner.

Now that the visuals are broken down, you can move on to the fun part — building the animation!

Note: This particular animation wasn’t too difficult to dissect, but it’s usually helpful to record an animation with QuickTime and then slow it down with After Effects or Adobe Photoshop to pick out the finer details. For example, this technique helped me discern whether the icon should disappear immediately when you tap a banner, or if it’s a more gradual disappearing act.

Laying Things Out

First, download the starter assets for this tutorial. This package contains all the assets needed to recreate the animation — the icons, the text and the cards.

First, install the Archive font included in the starter assets. It’s important to do this before you open the Sketch file, otherwise it won’t display correctly.

Next, open SweetStuff.sketch to see the assets I’ve created for you:


Go back to Framer and create a new file with File\New. Then click the Import button on the toolbar:
importSketch copy

Leave the export size at @1x and press Import again. You should see something like this:


Framer just created a variable named sketch that holds references to all the layers in your Sketch file you’ll need to create your prototype. You can see all these layers in the Layers Panel.

Note: Make sure that you have the Sketch file opened before you press Import, or otherwise Framer won’t be able to detect your Sketch file.

By default, the device selected for your prototype is an iPhone 6S Silver. However, your prototype should work on lots of other devices as well. You’ll create a device variable as a reference to the selected device, and you’ll lay things out relative to the device’s screen.

Add the following after the sketch variable:

device = Framer.Device.screen

Color names can be verbose, so define the following color constants at the top to make them friendlier to use:

blue = "rgb(97,213,242)"
green = "rgb(150,229,144)"
yellow = "rgb(226,203,98)"
red = "rgb(231,138,138)"

Next, create a container layer to hold everything. Add the following:

container = new Layer
	width: device.width
	height: device.height
	backgroundColor: 'rgba(255, 255, 255 1)'
	borderRadius: 5
	clip: true

This creates a container layer and sets its size equal to that of the device. Finally, you set the backgroundColor to white; this will serve as the background color for the cards. You’ll add all subsequent layers to the container layer.

By setting clip to true, nothing outside the bounds of the container will show.

Deselected State

Onwards to the fun part! You’ll start by creating the deselected screen first.


Start with the menu layers. Since you know the width and height of the layers, define them as constants as follows:

menuHeight = container.height/4
menuWidth = container.width

Add the code below and observe what happens:

cookieMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: 0
	backgroundColor: blue

This creates a new layer for your cookie menu, gives it the backgroundColor blue and sets the x origin, the y origin, the width and the height.

Now you’ll do the same thing for the other menus, but keep in mind that the y has to start at the end of the previous layer. The y-position of the current layer is y: previousMenu.y + previousMenu.height:

Add the following code to create the other menus:

cupcakeMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: cookieMenu.y + cookieMenu.height
	backgroundColor: green

fruitMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: cupcakeMenu.y + cupcakeMenu.height
	backgroundColor: yellow
iceCreamMenu = new Layer
	height: menuHeight
	width: menuWidth
	x: 0
	y: fruitMenu.y + fruitMenu.height
	backgroundColor: red

Next, add some shadow to create the illusion of the menus being on top of one another.

Add the following to the end of every layer definition:

	shadowY: 2
	shadowBlur: 40
	shadowSpread: 3
	shadowColor: "rgba(25,25,25,0.3)"

Hooray! Shadow! But… it’s going the wrong way. That’s because the layers were added on top of each other from top to bottom, with the ice cream menu on top, rather than the other way around.


Create a function to reposition the menus and bring them on top in the correct order. The signature for a function definition in Framer looks like this:

functionName = ([params]) ->

Add the following function to reposition the menus after your layer definitions:

repositionMenus = () ->


The repositionMenus function takes no parameters and brings the menus layers to the front in the order from bottom to top. At the end, you call the function.

Check out the prototype panel – now the shadows should appear in the proper order.


Adding Icons and Titles

Now let’s add some icons and titles to your menus, starting with the cookie menu.

Add the following code to the end of your file:

cookieIcon = sketch.Cookie
cookieIcon.superLayer = cookieMenu

cookieText = sketch.CookieText
cookieText.superLayer = cookieMenu

This adds two new variables: cookieIcon and cookieText and sets them equal to the corresponding sketch layers, Cookie and CookieText. You then set the superLayer of both to container.

Your next task is to position these layers now. cookieIcon should go in the center of its superLayer, and cookieText should center horizontally, but position itself 4/5ths of the way down its superLayer. The icon should go in the center of the layer’s superLayer.

Add the following code to center the icon:

Add the following code to set the position of the text;

cookieText.y = cookieText.superLayer.height * 0.8

Now just repeat this for the rest of the menus, using the following code:

cookieIcon = sketch.Cookie
cookieIcon.superLayer = cookieMenu

cookieText = sketch.CookieText
cookieText.superLayer = cookieMenu
cookieText.y = cookieText.superLayer.height * 0.8

cupcakeIcon = sketch.Cupcake
cupcakeIcon.superLayer = cupcakeMenu

cupcakeText = sketch.CupcakeText
cupcakeText.superLayer = cupcakeMenu
cupcakeText.y = cupcakeText.superLayer.height * 0.8

fruitIcon = sketch.Raspberry
fruitIcon.superLayer = fruitMenu

fruitText = sketch.FruitText
fruitText.superLayer = fruitMenu
fruitText.y = fruitText.superLayer.height * 0.8

iceCreamIcon = sketch.IceCream
iceCreamIcon.superLayer = iceCreamMenu

iceCreamText = sketch.IceCreamText
iceCreamText.superLayer = iceCreamMenu
iceCreamText.y = iceCreamText.superLayer.height * 0.8

This should give you something that looks very much like the deselected state:


Whew! You made it. Give yourself a little pat on the pack for getting this far. :]


But, in retrospect, that’s a whole lot of code to make one screen…


Just think of how unwieldy your code might be when you’ll have to juggle all the state changes and other details.

Preposterous! This looks like a good time to refactor your code.

Instead of creating each menu layer and its icon and title separately, you’ll create some helper functions to make the code look neater and easier to read.

Replace everything you’ve done so far after the menuHeight and menuWidth definitions with the following:

# 1
menuItems = []
colors = [blue, green, yellow, red]
icons = [sketch.Cookie, sketch.Cupcake, sketch. Raspberry, sketch.IceCream]
titles = [sketch.CookieText, sketch.CupcakeText, sketch.FruitText, sketch.IceCreamText]

# 2
addIcon = (index, sup) ->
	icon = icons[index]
	icon.superLayer = sup = "icon"

# 3
addTitle = (index, sup) ->
	title = titles[index]
	title.superLayer = sup
	title.y = sup.height - sup.height*0.2 = "title"

# 4
for menuColor, i in colors
	menuItem = new Layer
		height: menuHeight
		width: menuWidth
		x: 0
		y: container.height/4 * i
		shadowY: 2
		shadowBlur: 40
		shadowSpread: 3
		shadowColor: "rgba(25,25,25,0.3)"
		superLayer: container
		backgroundColor: menuColor
		scale: 1.00 
	addIcon(i, menuItem)
	addTitle(i, menuItem)

repositionMenus = () ->


Let’s review this section by section:

  1. This sets up some arrays to keep track of the menus, colors, icons and titles.
  2. This is a helper function to insert the icon sublayer for each menu item.
  3. This is a helper function to add the title sublayer for each menu item.
  4. This loops through each menu item, creating a new layer and calling the helper functions to add the icon and title. Note that it stores each layer in a menuItems array so you can easily access them later.

And this, ladies and gentleman, is clean code. Onwards to the next challenge!

Transitioning to Selected State

The first step is to add a new state named collapse to the main menuItems loop. Think about this for a second though. What do you need to do to menuItem when it enters the collapse state?


You’ll need to transition from an expanded state to a collapsed state.


Review the changes from before:

  • The y-position of the layer becomes 0.
  • The height goes from 1/4th of the screen to about 1/9ths of the screen.
  • The icon disappears gradually.
  • The y-position of the text changes so the text moves up.
  • You only see the shadow of the selected menuItem

Focus on the easy things first: the height and y-positions for the menuItem. Comment out the two following lines in the for loop, but don’t remove them — you’ll need them later.

# 	addIcon(i, menuItem)
# 	addTitle(i, menuItem)
Note: To comment out a line, press Command + / on the line.

Add the following collapsedMenuHeight constant with the other constants after the container layer:

collapsedMenuHeight = container.height/9

Add the collapse state right before menuItems.push(menuItem), just after the commented out parts:

			height: collapsedMenuHeight
			y : 0

Now to make the menuItems listen and respond to tap events.

Define the following tap events on each menuItem, just after the menuItem for loop and before repositionMenus:

#onTap listeners
menuItems[0].onTap ->
	for menuItem in menuItems

menuItems[1].onTap ->
	for menuItem in menuItems
menuItems[2].onTap ->
	for menuItem in menuItems

menuItems[3].onTap ->
	for menuItem in menuItems

Every time you tap on a menuItem, you loop through all the menuItems and transition each of them to its next state. this.bringToFront() brings the tapped menuItem to the top. By declaring each tap event separately, it’s easier to change them later.

Note: The keyword this is useful when you’re trying to refer to an object you’re manipulating, instead of using its name explicitly. It’s clearer and, in most cases, shorter and it increases the legibility of your code.

Give it a try to see how your touch interaction works so far:


Finishing Touches

So far so good, except you need to add the icon and titles back, and fix a few issues.

To do this, you’ll need to track when a menuItem is selected.

Add the following variable after the menuItems for loop and before the onTap listeners:

selected = false

You initially set selected to false since nothing is selected when you start.

Now you’re ready to start writing the function for switching between selected and deselected states. Add the following code before the repositionMenus function, after the onTap listeners:

# 1
menuStateChange = (currentItem) ->
	# 2
	for menuItem in menuItems
	# 3
	if !selected
	# 4
	# 5
	selected = !selected

This function does the following:

  1. Accepts a parameter currentItem, which is the tapped menuItem.
  2. Iterates through all menuItems and makes each transition to its next state.
  3. If no menuItem was selected, then selected is false, so you place currentItem at the front.
  4. If a menuItem was selected, then selected is true, so you return the menus to their default states with repositionMenus().
  5. Finally, you flip the state of the selected Boolean.

Now you can leverage this function in your onTap implementation. Change the onTap implementation for each as shown below for each menuItem instance, 0 through 3:

menuItems[0].onTap ->

Awesome. Now if you look closely, you may notice that when the menu items are compressed, the shadow looks particularly heavy. This is because all four layers have shadows that are stacking on top of each other.


To fix this, in menuStateChange, change the for loop as follows:

for menuItem in menuItems
	if menuItem isnt currentItem
		menuItem.shadowY = 0
		menuItem.shadowSpread = 0
		menuItem.shadowBlur = 0

This hides the shadow for any layer that isn’t the topmost layer, when the layers are collapsed.

Even though the animation looks pretty cool by now, there’s still two key things missing: the icon and the text.

Uncomment the below code found in the menuItems for loop (make sure that these are the last lines in the for loop):

addIcon(i, menuItem)
addTitle(i, menuItem)

Remember when you added names to the icon and title sublayers in addIcon and addTitle? Here’s where those come in handy. Those names will help you distinguish between different sublayers in a menuItem.

Add the following function to collapse the menu, just after menuStateChange():

collapse = (currentItem) ->
	# 1
	for menuItem in menuItems 
		# 2
		for sublayer in menuItem.subLayers
			# 3
			if is "icon"
						scale: 0
						opacity: 0
						time: 0.3
			# 4
			if is "title"
						y: collapsedMenuHeight/2
					time: 0.3

Here’s what’s going on in the code above:

  1. Iterate through each of the menuItems.
  2. For each menuItem, iterate through its subLayers.
  3. If you hit the icon sublayer, animate the scale to 0 and the opacity to 0 over the space of 0.3 seconds.
  4. When you hit the title sublayer, animate the y-position to the middle of the current menu.

Now, add the following function immediately below the one you just added:

expand = () ->
	# 1
	for menuItem in menuItems 
		# 2
		for sublayer in menuItem.subLayers
			# 3
			if is "icon"
						scale: 1
						opacity: 1
					time: 0.3
			# 4
			if is "title"
						y: menuHeight * 0.8
					time: 0.3

Taking it step-by-step:

  1. Iterate through each of the menuItems.
  2. For each menuItem, iterate through its subLayers.
  3. If you hit the icon sublayer, animate the scale to 100% and the opacity to 1 over the space of 0.3 seconds.
  4. When you hit the title sublayer, animate the y-position to menuHeight * 0.8.

Add the calls to collapse() and expand() to menuStateChange() as shown below:

menuStateChange = (currentItem) ->
	# remove shadow for layers not in front
	for menuItem in menuItems
		if menuItem isnt currentItem
			menuItem.shadowY = 0
			menuItem.shadowSpread = 0
			menuItem.shadowBlur = 0
	if !selected
	selected = !selected

Check out the prototype panel and now you’ll see the icon and title animate properly as well:


You’re almost at the finish line! You don’t have much farther to go! :]

Animation Settings

You can animate every layer in Framer; you can fine tune the animation by setting the following values:

  • properties: width, height, scale, borderRadius and others; the layer will animate from whatever state it is in to what you define here.
  • time: How long the animation lasts.
  • delay: If there should be a delay before the animation, and how long the delay should be.
  • repeat: How many times to repeat the animation.
  • curve: How fast the animation should run.
  • curve options: Fine-tuning options for the animation curve.

The most confusing two above are the curve and the curve options setting. You can use this tool to prototype animation curves with options: Framer.js Animation Playground.

Since you haven’t defined an animation curve for any of the animations, the prototype uses Framer’s default curve, ease:


This curve makes the prototype look stiff and unnatural. The curve you want is a spring curve that lets you have more control over what happens at each step of the transformation.

Time to translate the words used to describe these animations into numbers through the curveOptions settings.

This animation calls for a simple spring curve with the following parameters:

  • tension: How “stiff” the curve should be. The bigger the number, the more speed and bounce the animation should have.
  • friction: How much resistance to apply. The bigger the number, the more damped the animation will be.
  • velocity: The initial velocity of the animation.

Often you won’t just know what these numbers are. You will have to play around until you’re satisfied.

There are two animations with different speeds going on here:

Animating the menus: Starts slow, speeds up a lot, then slows down dramatically:


Animating the icon and title: The icon size goes from 100% to 0%, and the animation is fairly sudden. It starts fast, but finishes with a lot of damping:


Compared to the previous animation, this one has a lot less tension and the transitions between the different speeds are quicker. Give it a similar friction number for that nice damping effect and a small initial velocity.

Add the following just before menuItems.push(menuItem):

menuItem.states.animationOptions =
	curve: "spring"
		tension: 200
		friction: 25
		velocity: 8
	time: 0.25

This sets a spring animation for the menus and sets tension to 200, friction to 25, and velocity to 8. It runs just a little faster than the icon and title animations.

Find each instance of sublayer.animate, and add the following under the time line in the properties section, matching the indentation:

curve: "spring"
	tension: 120
	friction: 18
	velocity: 5

This will add a similar spring animation to the titles and icons, but they will be a little less springy and move a little slower than the menu sections.

You’ll add this code in four times in total: twice under collapse and twice under expand for both the icon and the title subLayers.

To compare your results, here’s an example of the collapse function with the required indentation:

collapse = (currentItem) ->
	for menuItem in menuItems 
		for sublayer in menuItem.subLayers
			if is "icon"
						scale: 0
						opacity: 0
					time: 0.3
					curve: "spring"
						tension: 120
						friction: 18
						velocity: 5
			if is "title"
						y: collapsedMenuHeight/2
					time: 0.3
					curve: "spring"
						tension: 120
						friction: 18
						velocity: 5

Here’s how things should look after you’re done:


Where to Go From Here?

And that’s a wrap! Congrats on your first Framer prototype :]

You can download the finished project here. It goes a bit further than this tutorial in that it shows you how to display the cards inside each menu item as well. The complete project, along with this same prototype created in Xcode + Swift are included.

If you have any questions or comments on this tutorial, please feel free to join the discussion in the forums below!

Lea Marolt Sonnenschein

Lea is a Product Manager for mobile at Rent the Runway. She writes about iOS, UX and UI, teaches iOS classes at GA and volunteers for Girls Who Code. In her free time she plays piano, and tries to use code and technology to make art. Or she just doodles and listens to MIKA.

Other Items of Interest

Big Book SaleAll iOS 11 books on sale for a limited time! Weekly

Sign up to receive the latest tutorials from each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 19 total!

iOS Team

... 71 total!

Android Team

... 17 total!

Unity Team

... 11 total!

Articles Team

... 15 total!

Resident Authors Team

... 18 total!

Podcast Team

... 7 total!

Recruitment Team

... 9 total!