Basic UIView Animation with Swift Tutorial

Bjørn Ruud
Use animation to see what's inside this picnic basket!

Use animation to see what's inside this picnic basket!

Update 2014-12-14: Updated for Xcode 6.1.1 and fixed tap gesture crash bug.

Update note: This tutorial was updated for iOS 8 and Swift by Bjørn Ruud. Original post by Malek Trabelsi.

One of the coolest things about iOS apps is how animated many of them are. You can have views fly across the screen, fade in or fade out, rotate around, and much more!

Not only does this look cool, but animations are good indicators that something is going on that a user should pay attention to, such as more information becoming available.

The best part about animation in iOS is that it is incredibly easy to implement programmatically! It’s literally just a couple lines of code and you’re up and running.

In this tutorial you’ll get a chance to go hands-on with UIView animation to create a simple app about going on a picnic. The picnic basket opens in a neat animated way, and then you get to look what’s inside – and take decisive action!

In the process you will learn how to use the basic UIView animation APIs, and how to chain animations.

So grab your picnic basket and let’s get started!

Getting Started

Just so you can appreciate how nice and easy UIView animation is, bear in mind that you would need to perform a few steps in order to animate a view moving across the screen if iOS didn’t provide you with built-in animation support:

  • Schedule a method to be called in your app, for every frame.
  • Every frame, calculate the new position of the view (x and y) based on the desired final destination, the time to run the animation, and the time run so far.
  • Update the view’s position to the calculated position by running the desired animation.

That’s not a ton of work, but it might make you think twice about implementing an animation system yourself. Plus, it gets a lot more complicated to keep track of the more animations you do.

But don’t worry – animations are extremely easy to use in UIKit! There are certain properties on views, such as the view’s frame (its size and position), alpha (transparency), and transform (scale/rotation/etc.) which have built-in animation support when they are modified within an animation block. Instead of having to do all of the manual animation steps above, you simply:

  • Set up an animation block, specifying how long it should run and a few other optional parameters.
  • Set an animatable property on a view within the block, such as its frame.

That’s it – UIKit will take over and handle the calculations and updates for you!

Let’s dive in and see what this looks like in code by creating an animation of a picnic basket opening when you start up the app.

An Opening Picnic Basket

Open up Xcode and select File \ New Project. Choose iOS \ Application \ Single View Application and click Next. Name the project Picnic, select Swift for language and iPhone for device. Click Next, choose a place to save your project and click Create.

For this project you must enable app scaling on iPhone 6 and 6 Plus so that you don’t have to worry about different device widths. Simply delete the LaunchImage.xib file and delete the file name from Launch Screen File in the General section of your target settings. In the same section, where it says Launch Images Source, press the Use Asset Catalog button, select the Images asset catalog in the dialog box and press Migrate.

Next, download a copy of some images and sounds made by Vicki that you’ll need for this project. In Xcode, select the Images.xcassets asset catalog. Unzip the downloaded archive and drag all files in the Images folder into the asset catalog view.

From the project navigator, click on Main.storyboard to open it in the editor. You will notice that the view shown is rectangular instead of a size that fits a device. In Xcode 6 storyboards will by default use size classes, which means the same storyboard can be used for all target devices regardless of screen size. That’s a pretty nice feature, but unnecessary for this project so let’s keep it simple.

With the storyboard selected, open the File inspector in the right hand pane. In the Interface Builder Document section there is a checkbox marked Use Size Classes. Uncheck it, make sure iPhone is selected as the new target in the dialog, then click Disable Size Classes.

You also need to disable autolayout by unchecking the checkbox marked Use Auto Layout. For simple layouts like this auto layout can make things more complex than necessary, and it is also beyond the scope of this tutorial. To learn about auto layout take a look at Beginning Auto Layout Tutorial in iOS 7.

Now you need to set up the user interface. You will start with the basket doors that will open to reveal the contents of the basket. First drag a View to the main view and resize it to completely fill its parent view. This view will serve as a container for the basket doors. Go to the Size inspector and uncheck all springs and struts so that the view “floats” in the center of the parent view.

Drag two UIImageViews to the container view, one on top filling up about half the space, and one on the bottom filling up the bottom half. Select the top image and open the attributes inspector. Set the image to door_top and set View Mode to Bottom. Set the bottom image to door_bottom, set View Mode to Top. Resize the image views until they look OK, as shown below.

Align the top and bottom parts of the basket.

Align the top and bottom parts of the basket.

By putting the image views in a container centered in the view the basket doors will always be centered on the screen regardless of screen size.

It’s time for some code. You need to declare properties for the two new image views in order to animate them later. Open the assistant editor and make sure ViewController.swift is shown. Ctrl-drag the top image view into the assistant editor, just below the class declaration, and name the outlet basketTop. Do the same for the bottom image view and name it basketBottom. The result should look like this:

class ViewController: UIViewController {
  @IBOutlet weak var basketTop: UIImageView!
  @IBOutlet weak var basketBottom: UIImageView!

Now that you have properties referencing the image views you created in Interface Builder you can animate them to open the basket when the view first appears. Switch back to ViewController.swift and add the following method below viewDidLoad():

override func viewDidAppear(animated: Bool) {
  UIView.animateWithDuration(0.7, delay: 1.0, options: .CurveEaseOut, animations: {
    var basketTopFrame = self.basketTop.frame
    basketTopFrame.origin.y -= basketTopFrame.size.height
    var basketBottomFrame = self.basketBottom.frame
    basketBottomFrame.origin.y += basketBottomFrame.size.height
    self.basketTop.frame = basketTopFrame
    self.basketBottom.frame = basketBottomFrame
  }, completion: { finished in
    println("Basket doors opened!")

The animateWithDuration(delay:, options:, animations:, completion:) call defines an animation with a duration of half a second that starts after a delay of one second. The animation is set to “ease out”, which means the speed slows down at the end.

The animations block defines what the animation will do. Here, the frames of the two image views are set to their final destinations: the top basket image moves up and the bottom one moves down to “open” the basket. Since you set the duration, UIKit takes over from there and runs a neat animation of your basket opening up.

The completion block runs after the animation completes or is interrupted – the finished parameter is a Boolean that will let you know if the animation finished or not.

Build and run the code to try it out for yourself – pretty easy to get such a neat effect, eh?

A Second Layer of Fun

When you go out on a picnic you usually don’t just throw your food straight into the basket – instead you put a little napkin on top to shield the food from pesky infiltrators. So why not have a little more fun with animations and add a napkin layer to open as well?

Go back to Main.storyboard, and select the two image views you worked with so far. With the image views selected, go to the Edit menu and select Duplicate. Position these duplicate views so they overlap with the originals.

From the Attributes inspector, set the new top view’s image to fabric_top and the new bottom view’s image to fabric_bottom.

You want the fabric views to be underneath the picnic basket. Open the document outline if it isn’t open already by navigating to Editor \ Show Document Outline. Since views are listed from bottom to top you want the napkin to be below the basket. Drag the views around in the outline so the order is as follows:

  • fabric_top
  • fabric_bottom
  • door_top
  • door_bottom

Now that you have the new image views in your scene, can you figure out how to animate this yourself based on everything you’ve learned? The goal is to make the napkin move off screen also, but start moving slightly after the picnic basket starts moving. Go ahead – you can always check back here if you get stuck.

The Solution

In case you had any troubles, here’s the solution.

First add two new outlet properties to ViewController.swift by ctrl-dragging from the storyboard to the assistant editor as you did for the basket outlet properties:

@IBOutlet weak var fabricTop: UIImageView!
@IBOutlet weak var fabricBottom: UIImageView!

Add the following to the bottom of viewDidAppear():

UIView.animateWithDuration(1.0, delay: 1.2, options: .CurveEaseOut, animations: {
  var fabricTopFrame = self.fabricTop.frame
  fabricTopFrame.origin.y -= fabricTopFrame.size.height
  var fabricBottomFrame = self.fabricBottom.frame
  fabricBottomFrame.origin.y += fabricBottomFrame.size.height
  self.fabricTop.frame = fabricTopFrame
  self.fabricBottom.frame = fabricBottomFrame
}, completion: { finished in
  println("Napkins opened!")

No big changes compared to the basket door animation you already worked with. Note that there are slightly larger values to the duration and delay parameters so they start and finish after the basket animation.

Build and run your code, and you should see the basket open in an even cooler manner!

How To Chain Animations

So far you’ve just been animating a single property on these UIViews – the frame. Also, you’ve done just a single animation, and then you were done.

However as mentioned earlier in this article, there are several other properties you can animate as well, and you can also trigger more animations to run after one animation completes. So let’s try this out by experimenting with animating two more interesting properties (center and transform) and using some animation chaining!

But first – let’s add the inside of the picnic basket! Open up Main.storyboard and drag yet another image view to the view container. Resize and position it so it fills the view, and make sure it’s at the top of the list so it’s shown underneath everything else. Set the image to plate_cheese.

There’s one more thing you have to add. Somehow, despite all of your precautions, a sneaky bug has made its way into the basket! Add another UIImageView as a subview of the container view. Put it right below the Plate view, and set the image to bug. Set its frame to x: 160, y: 185, width: 129, height: 135 in the Size inspector.

At this point, your View Controller Scene should look like the following in the document outline:

  • plate_cheese
  • bug
  • fabric_top
  • fabric_bottom
  • door_top
  • door_bottom

Next add an outlet for the new pest image view in ViewController.swift by ctrl-dragging from the the storyboard to the assistant editor, like you did for the other outlets. Call the outlet bug.

Eventually, you’ll get to squash the bug invading the picnic. Switch to ViewController.swift and add the following property inside the class declaration:

var isBugDead = false

This will be used later to determine whether the bug is dead or not.

Next, add the following methods:

func moveBugLeft() {
  if isBugDead { return }
    delay: 2.0,
    options: .CurveEaseInOut | .AllowUserInteraction,
    animations: { = CGPoint(x: 75, y: 200)
    completion: { finished in
      println("Bug moved left!")
func faceBugRight() {
  if isBugDead { return }
    delay: 0.0,
    options: .CurveEaseInOut | .AllowUserInteraction,
    animations: {
      self.bug.transform = CGAffineTransformMakeRotation(CGFloat(M_PI))
    completion: { finished in
      println("Bug faced right!")
func moveBugRight() {
  if isBugDead { return }
    delay: 2.0,
    options: .CurveEaseInOut | .AllowUserInteraction,
    animations: { = CGPoint(x: 230, y: 250)
    completion: { finished in
      println("Bug moved right!")
func faceBugLeft() {
  if isBugDead { return }
    delay: 0.0,
    options: .CurveEaseInOut | .AllowUserInteraction,
    animations: {
      self.bug.transform = CGAffineTransformMakeRotation(0.0)
    completion: { finished in
      println("Bug faced left!")

You can see that the way animation chaining is done is by starting a new animation in the completion block. The chain moves the bug left, rotates right, moves right, rotates left, and then repeats.

Take note of the .AllowUserInteraction option in the options parameter. This is because you need the bug view to respond to touch events (to get squashed :] ) while it’s moving, so that option allows you to interact with views while they are being animated.

The code moves the bug by modifying the center property rather than the frame property. This sets where the center of the bug image is, which can sometimes be easier to work with than setting the frame.

Bug rotation is done by setting an affine transform. CGAffineTransformMakeRotation is a helper function that creates a rotation transform, and the angle parameter is in radians which is why the parameter value is M_PI to rotate 180 degrees.

Now you just need to start the animation chain. Add the following line to the bottom of viewDidAppear():


Build and run, and you should see the bug scurrying about!



Squash the Bug!

Now it’s the moment I know you’ve been waiting for – it’s time to squash that bug!

Add the following method to handle the bug-squashing event:

func handleTap(gesture: UITapGestureRecognizer) {
  let tapLocation = gesture.locationInView(bug.superview)
  if bug.layer.presentationLayer().frame.contains(tapLocation) {
    println("Bug tapped!")
    // add bug-squashing code here
  } else {
    println("Bug not tapped!")

When responding to a tap, you need to check whether the tap was on the bug. Normally, you would check the tapLocation against the bug view frame but here, notice you’re using the view presentation layer’s frame. This is an important distinction: UIView animations will update a view’s “presentation layer” which represents what is shown on screen rather than the view’s frame itself. Technically, the bug is located exactly where it was positioned and the in-between animation is just a presentation layer change.

To find out more about layers and what this all means you can check out Introduction to CALayers.

In the top of the class, right below var isBugDead = false, add a tap optional to hold the gesture recognizer.

let tap: UITapGestureRecognizer?

Create the tap gesture recognizer in init, right below super.init. It has to be done after super.init since one of the parameters is self and you cannot use a reference to self before the object is initialized.

tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))

Next, add the following lines to the end of viewDidAppear():


This code initializes a new tap gesture recognizer and then adds it to the view.

Build and run, and tap on the screen. Depending on whether you hit the bug or not, you’ll see “Bug tapped!” or “Bug not tapped!” in the Xcode console.

The most satisfying part is next. Find handleTap() and add the following code inside the if block where the bug is tapped:

if isBugDead { return }
isBugDead = true
UIView.animateWithDuration(0.7, delay: 0.0, options: .CurveEaseOut, animations: {
  self.bug.transform = CGAffineTransformMakeScale(1.25, 0.75)
}, completion: { finished in
  UIView.animateWithDuration(2.0, delay: 2.0, options: nil, animations: {
    self.bug.alpha = 0.0
  }, completion: { finished in

Once the bug is tapped, you first set isBugDead to true so the animation chain stops running. Next the tap gesture recognizer is removed from the view so that no more interaction can happen. Then you can start a new chain of animations: first the bug is squished by applying a scale transform, and then it fades away by setting the alpha to 0 after a delay. After the fade the bug is removed from the superview.

Build and run, and now you should be able to squash the bug!



Gratuitous Sound Effect

This is totally unnecessary, but also totally fun – you’ll add a gratuitous bug squashing sound effect!

First drag the Sounds folder from the asset archive into the project, and make sure to select that files should be copied and groups created.

Back in ViewController.swift, add this import to the top of the file:

import AVFoundation

AVFoundation includes audio playback, so you need that import for maximum bug-squashing effect.

Next, add a property to the class declaration:

let squishPlayer: AVAudioPlayer

This will hold the audio player instance with your sound file.

Next, add the following initializer method:

required init(coder aDecoder: NSCoder) {
  let squishPath = NSBundle.mainBundle().pathForResource("squish", ofType: "caf")
  let squishURL = NSURL(fileURLWithPath: squishPath!)
  squishPlayer = AVAudioPlayer(contentsOfURL: squishURL, error: nil)
  super.init(coder: aDecoder)

This code ensures the AVAudioPlayer instance is set up when the view controller is initialized. If you know Objective-C it might seem strange to call super.init() at the end of the initalizer. In Swift an initalizer must always ensure all instance variables are initalized before the superclass initializer is called, and after that it’s safe to call instance and superclass methods. Note that instance variables with default values (like isBugDead) do not need to be set in a designated initializer.

Finally, add the following line to handleTap() just after you set isBugDead to true:

That will play the sound at just the right moment. And that’s it! Build and run your code, make sure the volume is turned up, and you can now squash the bug and get major audio satisfaction in the process.

Where To Go From Here?

Here is the final project with all of the code for this tutorial.

You can think of additional animations based on what you learned so far. For example, how about closing the picnic basket after squashing the bug (if you felt disgusted like me once you see the bug playing around the food ;] ). You can also change when to open up the basket doors and napkins, so that it will remain closed until you touch it.

Throughout this tutorial you only used the animateWithDuration(duration, delay, options, animations, completion) function, but there are also other animation functions available:

  1. animateWithDuration(duration, animations)
  2. animateWithDuration(duration, animations, completion)
  3. animateKeyframesWithDuration(duration, delay, options, animations, completion)
  4. performSystemAnimation(animation, onViews, options, animations, completion)
  5. animateWithDuration(duration, delay, usingSpringWithDamping, initialSpringVelocity, options, animations, completion)

The first two are the same as the animation method you have used in this tutorial, except without delay and options parameters. The third is for keyframe-based animation which enables almost total control over how things are animated. The fourth is for running a system provided animation. The last one creates an animation using a spring-based motion curve. In other words, it bounces towards the end.

Now that you know the basics of using UIView animations you might want to take a look at the Animations section in the View Programming Guide for iOS for additional useful info.

How do you use UIView animation or Core Animation in your projects? I’d love to hear from you in the forum discussion below!

Bjørn Olav Ruud

Bjørn is an iOS programmer from Norway. During the day he's a freelance iOS programmer and consultant. At night he experiments with Swift and games.

In his spare time Bjørn enjoys games (computer/card/board) for his mental health, and strength training, parkour and other activities that are likely to cause injury for his physical health.

Blog | GitHub | LinkedIn

Other Items of Interest 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!

Swift Team

... 16 total!

iOS Team

... 31 total!

Android Team

... 15 total!

macOS Team

... 11 total!

Apple Game Frameworks Team

... 10 total!

Unity Team

... 11 total!

Articles Team

... 11 total!

Resident Authors Team

... 11 total!