How to Create an iOS Book Open Animation: Part 2

Learn how to create an iOS book open animation including page flips, with custom collection views layout and transitions. By Vincent Ngo.

Leave a rating/review
Save for later
Share

Learn how to create a cool book opening animation!

Welcome back to our iOS book open animation tutorial series!

In the first part of this tutorial series, you learned how to create two custom collection view layouts and applied shadow layers to the book’s pages to create depth and realism in your app.

In this final part, you’ll learn to create custom navigation transitions and apply interactive gestures to open a book with a pinch gesture.

Note: Full credit goes to Attila Hegedüs for creating this awesome sample project.

Getting Started

The tutorial picks up from Part 1. If you didn’t work through the last part, or want to start afresh, simply download the completed sample project from the previous tutorial.

VN2_Start

Open up the project in Xcode. Right now, when you select a book to read the open pages simply slide in from the right. This is the default transition behavior for a UINavigationController. But by the end of this tutorial, your custom transition will look like the following:

VN_BookOpening

The custom transition will animate the book smoothly between the closed and opened states in a natural manner that users will love.

Time to get started!

Creating your Custom Navigation Controller

To create a custom transition on a push or pop you must create a custom navigation controller and implement the UINavigationControllerDelegate protocol.

Right-click (or Ctrl-click) on the App group and click New File. Select the iOS\Source\Cocoa Touch Class template and name the new file CustomNavigationController. Make sure it’s a subclass of UINavigationController and set the language to Swift. Click Next and then Create.

Open CustomNavigationController.swift and replace its contents with the following:

import UIKit

class CustomNavigationController: UINavigationController, UINavigationControllerDelegate {

  override func viewDidLoad() {
    super.viewDidLoad()
    //1
    delegate = self
  }

  //2
  func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    if operation == .Push {
      return nil
    }

    if operation == .Pop {
      return nil
    }
    
    return nil
  }
}

Here’s what you’re doing in the code above:

  1. In viewDidLoad you set the navigation controller as its own delegate.
  2. navigationController(_:animationControllerForOperation:fromViewController:toViewController:) is one of the methods you can implement for UINavigationControllerDelegate. This method executes each time you push or pop between view controllers, and you control which animated transition you return from this method. The code currently returns nil which defaults to the standard transition. You’ll replace it with your own custom transition object shortly.

Now that you have your custom navigation controller set up, it’s time to replace the default navigation controller in storyboard.

Open Main.storyboard and click Navigation Controller in the storyboard’s view hierarchy on the left. Next, click the Identity Inspector and under Custom Class, change UINavigationController to CustomNavigationController, as shown below:

VN_storyboard2

Build and run to ensure everything still works; nothing will have changed since you’re returning nil in your delegate method, which defaults to the navigation controller’s standard transition.

Creating the Custom Transition

Time for the fun part — building your custom transition object! :]

With a custom transition object, the class you create must conform to the UIViewControllerAnimatedTransitioning protocol, and in particular, the methods below:

  • transitionDuration: Required. Returns the duration of the animation and synchronizes interactive transitions.
  • animateTransition: Required. Provides the to and from controllers you’re transitioning between. Most of the heavy lifting will be done in this method.
  • animationEnded: Optional. Informs you when the transition has finished. You can perform any required cleanup in this method.

Setting up Your Transition

Right-click (or Ctrl-click) on the App group and click New File. Select the iOS\Source\Cocoa Touch Class template and name the new file BookOpeningTransition. Make sure it’s a subclass of NSObject and set the language to Swift. Click Next and then Create.

Open BookOpeningTransition.swift and replace its contents with the following:

import UIKit

//1
class BookOpeningTransition: NSObject, UIViewControllerAnimatedTransitioning {
  
  // MARK: Stored properties
  var transforms = [UICollectionViewCell: CATransform3D]() //2
  var toViewBackgroundColor: UIColor? //3
  var isPush = true //4
  
  //5
  // MARK: UIViewControllerAnimatedTransitioning
  func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
    return 1
  }
  
  func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    
  }
}

Taking each commented section in turn:

  1. BookOpeningTransition implements the required methods for the UIViewControllerAnimatedTransitioning protocol.
  2. The dictionary transforms stores key value pairs, where the key is a UICollectionViewCell and the value is of type CATransform3D. This dictionary tracks each cell’s page transform when the book is open.
  3. This defines the color you transition to, which helps the fade look much cleaner.
  4. The boolean isPush determines whether the transition is a push, or a pop,
  5. Here you add the required methods for UIViewControllerAnimatedTransitioning to avoid build errors; you’ll implement these methods shortly.

Now that you have your variables set up, it’s time to implement the protocol methods.

Replace the contents of transitionDuration(_:) with the following:

if isPush {
  return 1
} else {
  return 1
}

transitionDuration(_:) returns the duration of the transition animation. In this case, you want it to take 1 second on either a push or a pop. Writing the method this way lets you easily change the timing of the push or pop.

Next, you need to implement the second required protocol method — animateTransition — where the magic will happen! :] You’ll implement this in two parts:

  1. Implement the helper methods to set up animateTransition for a push.
  2. Implement the helper methods to set up animateTransition for a pop.

Creating the Push Transition

Imagine yourself opening a book in real life:

VN_PushStage

Although it looks complicated, you only need to consider the two states of your animation and let UIView‘s method animateWithDuration handle the animation between the following two states:

  1. Stage 1 is when the book is closed.
  2. Stage 2 is when the book is open; this is essentially the transform you created in Part 1 of this tutorial.

First, you’ll implement some helper methods to handle the two states before you implement the animateTransition(_:) protocol method.

Still in BookOpeningTransition.swift, add the following code to the end of the class:

// MARK: Helper Methods
func makePerspectiveTransform() -> CATransform3D {
  var transform = CATransform3DIdentity
  transform.m34 = 1.0 / -2000
  return transform
}

This code returns a transform and adds perspective in the z-axis. You’ll use this later to help transform your views during the animation.