How To Create a Cool 3D Sidebar Animation Like in Taasky

Learn how to recreate the amazing 3D slide-out menu effect seen in the Taasky iOS app in Swift, using view controller containment and Core Animation.


  • Other, Other, Other

Learn how to make a stylish 3D animation like Taasky’s open-door sidebar!

Last year, readers voted on which effect from Evan Dekhayser’s Top 5 iOS 7 Animations they most wanted to see featured in a future tutorial — and the winner? How to achieve the cool 3D effect of Taasky’s slide-out menu!

This tutorial is for the experienced developer; you’ll be working with Auto Layout constraints, UIScrollView, view controller containment, and Core Animation. If this all sounds a bit unfamiliar, then I’d recommend you start with some of our other iOS tutorials before returning to this one!

Note: Special thanks to David Grandinetti and Mic Pringle for some of the ideas behind the sample project in this tutorial.

Getting Started

Download the starter project, extract it, and open it in Xcode.

Take a moment and have a poke around in the project; you’ll see it’s a master-detail app that displays a table of images. The UITableViewController subclass MenuViewController uses the custom table view cell MenuItemCell in order to set the background color of each cell. The DetailViewController displays a large image using the same background color as the selected cell, as shown below:


You could imagine this as a basic app for negotiating coffee breaks or after-work cocktails with your colleagues or friends; thumbs-up if you’re game, thumbs-down if you can’t make it – and you can even decline due to inclement weather. :]

Creating the feature in this tutorial isn’t as complicated as you might think. You’ll build it up slowly as follows:

  • First, you’ll use Auto Layout in Interface Builder to convert a basic master-detail app to a horizontal scroll view, with the master and detail views embedded in containers.
  • Next, you’ll add a button to show or hide the menu.
  • Then you’ll implement the 3D effect to acheive the neat Taasky-like folding effect!
  • As a final touch, you’ll piggy-back on the menu animation to rotate the menu button, in sync with the act of showing or hiding the menu.

Your first task is to convert the menu into a slide-out sidebar, similar to the SwiftSideNav sample app from the video tutorial Swift Scroll View School Part 13, where a scroll view contains the menu and detail views side-by-side, as shown below:


The user can scroll right or left to show or hide the menu. The purple rectangle outlines the visible content when the menu is open, and the green rectangle outlines the detail view, which is fully visible when the menu is hidden. When the menu is open, the partial detail view provides a visual cue that the menu is a sidebar.

Note: If you’re unfamiliar with scroll views then please watch Part 1 and Part 2 of the Swift Scroll View School video series to refresh your understanding of how scroll views work.

SwiftSideNav, one of the samples from the Swift Scroll View School video series, sets up the Auto Layout constraints programmatically. But in this tutorial, you’ll set them up in directly in the storyboard, embedding the menu and detail views in container views. It will look like this when you’re done:


Adding Containers to UIScrollView

Note: This section relies heavily on View Controller Containment, which was introduced in iOS 5. If you’re new to this concept, check out Chapter 18, “UIViewController Containment” in iOS 5 by Tutorials.

You’ll need a new view controller to coordinate the existing menu and detail view controllers. You’ll add a scroll view to its view, then add a Content view to the scroll view. Then you’ll add two container views to the Content view and embed the existing menu and detail views in these container views. To reduce errors and confusion, you’ll set up the Auto Layout constraints as you go.


Create a new Cocoa Touch Class file named ContainerViewController, making sure to subclass UIViewController. Also set the Language to Swift:


Note: You can zoom out the storyboard if you need to – right-click on an unoccupied part of the canvas and select one of the zoom options. Double-click anywhere on the empty canvas to return to the standard zoom. You can also zoom in and out using pinch gestures on a trackpad, or using the scroll wheel on your mouse if it has one.

Open Main.storyboard and drag a new View Controller from the Object Library onto the canvas to the left of the existing scenes. In the Identity Inspector, set Class\Custom Class to ContainerViewController:


Select your new View and set Background to Black Color in the Attributes Inspector:


Setting Up the Scroll View

Next, you’ll add a Scroll View to the view of ContainerViewController, then add its Auto Layout constraints.

In Interface Builder, drag a Scroll View onto ContainerViewController, and let it expand to fill the space. Next uncheck Shows Horizontal Indicator and Shows Vertical Indicator in the Attributes Inspector, to turn off the scroll view’s scroll bars. Also uncheck Delays Content Touches so that the menu will respond immediately when the user makes a selection, rather than having a slight delay:


Control-drag from the scroll view to its view controller, and select delegate in-order to set ContainerViewController as the scroll view’s delegate:


In the Pin constraints pop-up – found at the bottom of the Interface Builder window – uncheck Constrain to margins. Select the leading, top, trailing, and bottom constraints, make sure they’re values are all 0, and then click Add 4 Constraints to pin the four edges of the scroll view to its superview:


With the scroll view still selected, confirm the new constraints in the Size Inspector:

  • Trailing Space to: Superview
  • Leading Space to: Superview
  • Top Space to: Superview
  • Bottom Space to: Bottom Layout Guide


Numbers like -16 means the constraints are relative to the margin; fix these by deleting the necessary constraints and then re-pin the scroll view, being sure to uncheck Constrain to Margins.

Note: When you add or modify constraints, you might need to update the frame of a view to reflect the new constraints. Select the Resolve Auto Layout Issues button at the bottom of Interface Builder and select Update Frames as required.

Setting up the Content View

Now you’ll add a content view to the scroll view and set up its Auto Layout constraints; these constraints are important as they help determine the scroll view’s content size. In the next section, you’ll layout two container views in the content view to hold the menu and detail views.

Drag a new View onto the scroll view; let it expand to the size of the scene in Interface Builder, then set Background to Default in the Attributes Inspector:


Open the Identity Inspector for the content view you just added and set Document\Label to Content View; this label appears in the document outline and will help you keep track of the views, and it’s also the name referenced in Auto Layout contraints. You’ll place the two container views into Content View later.

Note: You’ll see Auto Layout warnings when you set up Content View’s constraints, but don’t panic as you’ll fix them before the end of the section.

Pin the four edges of Content View to its superview, which in this case is the Scroll View:


In the Size Inspector, ensure that the Trailing Space constraint’s Constant is set to 0:


The Auto Layout warning appears because the storyboard requires constraints on Content View’s height and width to determine the scroll view’s content size. Making these constraints relative to the scroll view’s superview lets the display adapt to various devices and orientations.

In the document outline, control-drag from Content View to View (Scroll View’s superview), hold down the Shift key, and select Equal Widths and Equal Heights:


Then edit the Equal Width constraint’s Constant to 80:


Setting the constant to 80 means the Content View will be 80 points wider than the viewable area of the View so it can accommodate the menu. You’ll see the Auto Layout warnings have now disappeared – good work! :]

Adding Menu and Detail Container Views

The scroll view now has a Content View for your two container views. You’ll embed the existing menu and detail views in these container views to create a menu sidebar you can swipe open or closed.

First, add the Menu Container View: drag a Container View onto Content View. In the Size Inspector, set Width to 80, then use the Identity Inspector to set Document\Label to Menu Container View:


The Height should default to 600, but if it doesn’t make sure to set that as well.

Next, add the Detail Container View: drag another Container View to the right of the menu container within Content View. Open the Size Inspector and set the following values:

  • X: 80
  • Y: 0
  • Width: 600
  • Height: 600

Then open the Identity Inspector and set the Document\Label to Detail Container View so you can easily identify the view:


The width of the two containers now equal the entire width of the Content View:


When you add a container view, Interface Builder will include a contained view controller scene by default; but you’ll connect your container views to the existing menu and detail view controllers. Delete the view controllers that are currently embedded in the container views:


Next, you’ll set up the Auto Layout constraints for the container views.

Pin the edges of Menu Container View its superview, and add a Width constraint with a constant of 80. You should add 5 constraints in total, as shown below:


Pin Detail Container View’s Top, Bottom, and Trailing edges; don’t pin the Leading edge, as it would duplicate the sibling Menu Container View’s Trailing constraint:


For both sets of contraints, make sure Constrain to margins in unchecked.

Embedding Menu and Detail View Controllers

In this section, you’ll separate the menu and detail view controllers, then embed them in Menu Container View and Detail Container View.

First, make Container View Controller the Initial view controller: drag the start arrow – which denotes the initial view controller in the storyboard – from the Navigation Controller to Container View Controller:


Next, embed control-drag from Menu Container View to Navigation Controller, and select embed from the pop-up menu:


Once the navigation controller is embedded in Menu Container View, each of the connected view’s widths in Interface Builder automatically shrink to 80 points:


Now to make some adjustments to the menu and detail scenes: first, change the Width of the table view cell’s image view to 80:


Next, delete the push segue between the menu and detail scenes. Select Detail View Controller, then select Editor\Embed In\Navigation Controller from the Xcode menu:


The Detail View Controller should now be the initial view controller of a new navigation controller, which will provide the black navigation bar with the icon to expand the menu.

Select the navigation bar of the new navigation controller and use the Attributes Inspector to set Style to Black, uncheck Translucent, and set Bar Tint to Black Color:


This adjusts this navigation bar to match the navigation bar of the first navigation controller.

Then, set up the new navigation controller to match the first navigation controller. In its Attributes Inspector, make sure View Controller\Layout\Adjust Scroll View Insets is checked:


Note: Checking Adjust Scroll View Insets causes the view’s content to start below the navigation bar, rather then underneath it.

Finally, embed Detail View Controller in Detail Container View. Control-drag from Detail Container View to Detail View Controller’s navigation controller, and select embed from the pop-up menu:


Build and run. Swipe left and right to hide or show the menu. Did you notice that you can scroll beyond the left and right edges, and stop scrolling with only part of the menu showing?


To fix these issues, change the following attributes of the scroll view using the Attributes Inspector:

  1. Check Scrolling\Paging Enabled so the container “snaps” to it’s boundaries.
  2. Uncheck Bounce\Bounces to prevent scrolling beyond the edges.

Build and run. Check that swiping right does nothing when the menu is open, and swiping left does nothing when the menu is closed. Also check that scrolling to part of the menu snaps it open. But what happens when you attempt to hide the menu?


It keeps snapping back open! This is a paging issue related to this problem discussion on StackOverflow. You’ll fix this in just a bit, once you create an outlet for the scroll view.

But there are other problems to fix first: the detail view is blank, and selecting a menu item doesn’t change anything:


This isn’t surprising, since you haven’t yet adjusted the code to use the container views. You’ll first make the detail view’s navigation bar match the menu view’s navigation bar.

Editing the Code to Use Containers

Before doing anything else, copy viewDidLoad() from MenuViewController.swift into DetailViewController.swift as shown below:

override func viewDidLoad() {
  // Remove the drop shadow from the navigation bar
  navigationController!.navigationBar.clipsToBounds = true

This eliminates the hairline shadow beneath the navigation bar. A small detail, but it’s one of those things that gives your app polish. :]

When the user selects a table cell, MenuViewController must set DetailViewController’s menuItem property, but DetailViewController isn’t directly connected to MenuViewController anymore. Instead, the ContainerViewController will act as the mediator between the menu and the content.

Add a property for the DetailViewController to the top of ContainerViewController.swift:

private var detailViewController: DetailViewController?

Implement prepareForSegue(_:sender:) in ContainerViewController.swift with the following code:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "DetailViewSegue" {
    let navigationController = segue.destinationViewController as! UINavigationController
    detailViewController = navigationController.topViewController as? DetailViewController

segue.identifier! what segue, where? Well, embedding DetailViewController in the container creates a Storyboard Embed Segue, and you must set its Identifier in the Attributes inspector.


Then declare the menuItem property at the top of the class, whose didSet observer in-turn sets DetailViewController’s menuItem property:

var menuItem: NSDictionary? {
  didSet {
    if let detailViewController = detailViewController {
      detailViewController.menuItem = menuItem

There’s no longer a segue from a table view cell to the content view, but MenuViewController needs to respond when the user selects an item.

Delete prepareForSegue(_:sender:) from MenuViewController and add the following method — just be careful not to accept tableView(_:didDeselectRowAtIndexPath:) from Xcode’s list of autosuggestions by accident! :]

// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  tableView.deselectRowAtIndexPath(indexPath, animated: true)
  let menuItem = menuItems[indexPath.row] as! NSDictionary
  (navigationController!.parentViewController as! ContainerViewController).menuItem = menuItem

Here, you simply set ContainerViewController’s menuItem property based on the selected row. This triggers the property’s didSet observer, which in-turn sets DetailViewController’s menuItem property.

As a final touch, add the following line to viewDidLoad() in MenuViewController.swift:

(navigationController!.parentViewController as! ContainerViewController).menuItem = 
  (menuItems[0] as! NSDictionary)

This sets the image in the detail view when the app is first launched.

Build and run. The detail view shows the smiley image on launch, the menu view appears along the left edge of the center view, and selecting an item displays its larger image in the detail view, as shown below:


Showing and Hiding Your Menu

The menu should dismiss when the user selects an item from the menu. To do this, you need to set the scroll view content’s horizontal offset to Menu Container View’s width so the scroll view shows only Detail Container View.

You’ll first need references to the scroll view and to Menu Container View.

Create an outlet named scrollView in ContainerViewController.swift: select Scroll View in the storyboard’s document outline, open the Assistant Editor, and control-drag from Scroll View into ContainerViewController.swift. Enter scrollView for Name and click Connect:


Note: I’ve’ used View\Assistant Editor\Assistant Editors on Bottom to place the Assistant Editor at the foot of the Xcode window, so I didn’t have to drag things across the storyboard canvas.

Select and control-drag from Menu Container View into ContainerViewController.swift, and create an outlet named menuContainerView:


Next, add the hideOrShowMenu(_:animated:) method to ContainerViewController.swift:

// MARK: ContainerViewController
func hideOrShowMenu(show: Bool, animated: Bool) {
  let menuOffset = CGRectGetWidth(menuContainerView.bounds)
  scrollView.setContentOffset(show ? CGPointZero : CGPoint(x: menuOffset, y: 0), animated: animated)

The value of menuOffset is 80 — the width of Menu Container View. If show is true, the origin of scrollView is at 0,0 and the menu is visible. Otherwise, the origin of scrollView is at 80,0 and the menu is hidden.

Now, call hideOrShowMenu(_:animated:) from the didSet observer on menuItem:

var menuItem: NSDictionary? {
  didSet {
    hideOrShowMenu(false, animated: true)
    // ...

The value of show is set to false to hide the menu when the user selects an item.

Also call hideOrShowMenu(_:animated:) in viewDidLoad() to hide the menu when the app starts:

override func viewDidLoad() {
  hideOrShowMenu(false, animated: false)

Build and run. It starts with the smiley image selected and the menu hidden. Slide out the menu and select an item; the detail view slides across, hides the menu and displays the new item’s image and background color:


However, that paging issue remains. If you slide out the menu and don’t select an item, closing the menu causes it to spring back open. You’ll fix this by employing a UIScrollViewDelegate method.

Add the following protocol to the class declaration of ContainerViewController:

class ContainerViewController: UIViewController, UIScrollViewDelegate {

Next, add the following scroll view delegate method to ContainerViewController:

// MARK: - UIScrollViewDelegate
func scrollViewDidScroll(scrollView: UIScrollView) {
  Fix for the UIScrollView paging-related issue mentioned here:
  scrollView.pagingEnabled = scrollView.contentOffset.x < (scrollView.contentSize.width - CGRectGetWidth(scrollView.frame))

This disables paging when the scroll view’s content offset equals the menu width; when the menu is completely hidden, it stays hidden. Once the user starts to slide out the menu it re-enables paging so the menu snaps open.

Build and run. The paging issue is now fixed:


Looking good! But there’s still something missing. The hamburger menu button in the detail view’s navigation bar that rotates as the menu moves. Tapping it should toggle the menu open or closed.

Adding the Menu Button

Note: The hamburger icon gets a lot of negative press as it hides functionality from users; see Why We Banished the Hamburger Menu From Our iPhone App, as an example. However, the original Taasky app featured it, so you get the full hamburger meal deal in this tutorial. :]

You'll need to set up the menu button as a custom view so that it can rotate when you reveal the menu.

Create a new Swift Cocoa Touch Class file named HamburgerView.swift, making sure to subclass UIView.

Populate the class with the following code:

class HamburgerView: UIView {

  let imageView: UIImageView! = UIImageView(image: UIImage(named: “Hamburger”))

  required init(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

  required override init(frame: CGRect) {
    super.init(frame: frame)

  // MARK: Private

  private func configure() {
    imageView.contentMode = UIViewContentMode.Center


Here you set up the required initializers, both of which call through to a utility method that simply creates an image view and adds it as a subview.

In DetailViewController.swift, add the following hamburgerView property:

var hamburgerView: HamburgerView?

In viewDidLoad(), create and add an instance of hamburgerView to the navigation bar as a left bar button, including a tap gesture recognizer:

let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: “hamburgerViewTapped”)
hamburgerView = HamburgerView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: hamburgerView!)

hamburgerViewTapped() will call ContainerViewController’s hideOrShowMenu(_:animated:), but what value should it pass as the show argument? You need a Bool property in ContainerViewController, to keep track of whether the menu is open or closed.

Add the folllowing property to the top of ContainerViewController.swift:

var showingMenu = false

The menu's hidden when the app starts. Override viewDidLayoutSubviews() to hide or show the menu when the bounds change, such as when the device rotates:

override func viewDidLayoutSubviews() {
  hideOrShowMenu(showingMenu, animated: false)

You no longer need viewDidLoad(), so delete it completely from ContainerViewController.swift.

Open DetailViewController.swift and add the following code:

func hamburgerViewTapped() {
  let navigationController = parentViewController as! UINavigationController
  let containerViewController = navigationController.parentViewController as! ContainerViewController
  containerViewController.hideOrShowMenu(!containerViewController.showingMenu, animated: true)

When the user taps the button, and the menu is hidden — if showingMenu equals false — then this method calls hideOrShowMenu(_:animated:) passing true to show the menu. Conversely, when the menu is open — showingMenu equals true — tapping the button will hide the menu.. Therefore, you must update the value of showingMenu in hideOrShowMenu(_:animated:) in ContainerViewController.swift.

Add the following line to hideOrShowMenu(_:animated:):

showingMenu = show

Build and run. Try different combinations of scrolling, selecting cells, and button-tapping to show and hide the menu:


There’s just one problem. If you slide the menu open or closed instead of using the button, it takes two taps of the button to get the menu to respond. Why?


This happens because scrolling doesn’t update the value of showingMenu. Swiping the menu open leaves the value at false. The first tap appears to do nothing as it sets the value to true, so the menu stays open. The second tap then hides the menu as it sets the value to false.

To fix this, you need to set showingMenu in one of the other UIScrollViewDelegate methods in ContainerViewController. Add the following:

func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
  let menuOffset = CGRectGetWidth(menuContainerView.bounds)
  showingMenu = !CGPointEqualToPoint(CGPoint(x: menuOffset, y: 0), scrollView.contentOffset)
  println(“didEndDecelerating showingMenu \(showingMenu)”)

When the scrolling stops, this delegate method sets showingMenu to false if the scroll view’s content offset equals the menu width — that is, the menu is hidden — otherwise it sets it to true.

Build and run. Swipe right and check the console to see if this method kicks in when you stop. It's a bit hit or miss, and seems to depend on the scrolling speed. When I tested the app in the simulator, it only worked when I scrolled slowly, but on my iPhone, it only worked when I scrolled quickly. Go figure! :]

Move the above statements into scrollViewDidScroll(_:), where it’s much less efficient - this method is called continously as the user scrolls - but far more reliable:


You'll get to the button rotation in just a bit — but first, you'll take care of the 3D menu effect.

Adding Perspective to Your Menu

The ultra-cool animated version of your menu should look similar to a door opening and closing. As well, the menu button will rotate smoothly clockwise as the menu opens, and counter-clockwise as the menu closes.

To accomplish this, you'll calculate the fraction of the menu view that's visible, then use this to calculate the menu's angle of rotation.

In ContainerViewController.swift, add the following private method to create the 3D transform, depending on the fraction of the menu shown:

func transformForFraction(fraction:CGFloat) -> CATransform3D {
  var identity = CATransform3DIdentity
  identity.m34 = -1.0 / 1000.0;
  let angle = Double(1.0 - fraction) * -M_PI_2
  let xOffset = CGRectGetWidth(menuContainerView.bounds) * 0.5
  let rotateTransform = CATransform3DRotate(identity, CGFloat(angle), 0.0, 1.0, 0.0)
  let translateTransform = CATransform3DMakeTranslation(xOffset, 0.0, 0.0)
  return CATransform3DConcat(rotateTransform, translateTransform)

Here's the play-by-play of transformForFraction(_:):

  • fraction is 0 when the menu is completely hidden, and 1 when the menu is completely visible.
  • CATransform3DIdentity is the 4x4 matrix with 1s on the diagonal and 0s everywhere else.
  • CATransform3DIdentity's m34 property is the value in row 3, column 4, which controls the amount of perspective in the transform.
  • CATransform3DRotate uses angle to determine the amount of rotation around the y-axis: -90 degrees renders the menu perpendicular to the back of the view and 0 degrees renders the menu parallel with the x-y plane.
  • rotateTransform rotates the identity transform by angle degrees around the y-axis by the amount noted in its m34 perspective parameter.
  • translateTransform moves the menu from half of its width to the correct position when rotation finishes.
  • CATransform3DConcat concatenates translateTransform and rotateTransform so that the menu appears to slide sideways while rotating.

Note: m34 is usually calculated as 1 divided by a number that represents your position on the z-axis while observing the 2D x-y plane. Negative z-values indicate the viewer is in front of the plane, while positive z-values indicate the viewer is behind the plane.

Drawing lines between this viewer and the edges of objects in the plane produces the effect of 3D perspective. As the viewer moves farther away, the perspective is less pronounced. Try changing 1000 to 500 or 2000 to see how the menu’s perspective changes.

Now add the following lines to scrollViewDidScroll(_:):

let multiplier = 1.0 / CGRectGetWidth(menuContainerView.bounds)
let offset = scrollView.contentOffset.x * multiplier
let fraction = 1.0 - offset
menuContainerView.layer.transform = transformForFraction(fraction)
menuContainerView.alpha = fraction

The value of offset is between 0 and 1. When it's 0 it means the menu is completely visible, and when it's 1 it means the menu is completely hidden. Think of it as a measure of the menu’s hiddenness - and yes, according to my Mac’s lookup, hiddenness really is a word! :]

Consequently, fraction is the visible fraction of the menu width, ranging between 0 when the menu is completely hidden and 1 when the menu is completely visible.

You also use fraction to adjust the menu's alpha value to darken and lighten the menu as it closes and opens respectively.

Build and run. Swipe to to see the 3D effect and...uh oh. There’s something wrong with the menu’s “hinge”, because the default anchorPoint of the view - the point at which the transform is applied - is in its centre.


To make the menu hinge around its right edge, add the following line to viewDidLayoutSubviews() in ContainerViewController.swift:

menuContainerView.layer.anchorPoint = CGPoint(x: 1.0, y: 0.5)

Setting the x value to 1.0 move it all the way to the right-edge.

Build and run. Bask in your glorious 3D folding animation!


One Last Thing: Animating the Menu Button


Now you can put the final finishing touch on your app by rotating the menu button.

When the menu is closed, you see the standard menu icon. When the menu opens, the button should rotate 90 degrees clockwise. When the menu closes, the button should rotate counter-clockwise back to 0 degrees.

Note: Technically speaking, it's the menu button's imageView that you rotate. But the resulting effect is that the button itself appears to rotate.

Add the following method to HamburgerView.swift:

func rotate(fraction: CGFloat) {
  let angle = Double(fraction) * M_PI_2
  imageView.transform = CGAffineTransformMakeRotation(CGFloat(angle))

Here you rotate the image view smoothly. You use fraction to calculate an angle between 0 and 90 degrees, and rotate the view accordingly. For the curious ones, M_PI_2 above is defined in math.h, and is the mathematical constant for pi/2.

Now add the following lines to scrollViewDidScroll(_:), to make the view rotate as you move the scroll view:

if let detailViewController = detailViewController {
  if let rotatingView = detailViewController.hamburgerView {

Build and run. Swipe and tap to see your animations in motion, and totally in sync:


Where to Go From Here?

You can download the final version of the project here.

Experiment with the m34 value to see what effect it has on your transformation. If you'd like to try out more 3D transforms, take a look at Richard Turton's Visual Tool for CATransform3D.

Wikipedia's Perspective page also has some good photos explaining the concepts of visual perspective.

Also, think about how you could use 3D animation in your own applications to add a little bit of life to user interactions. It's amazing how a subtle effect on something as simple as a menu can really add to the overall user experience.

If you have any questions or comments about this tutorial, please leave them in the comments below, or in the forums!