Implementing a custom modal presentation transition

A lot of applications implement modally-presented view controllers. A modally-presented view controller is typically a view controller that is presented on top of the current screen as an overlay. By default, modally-presented view controllers animate upward from the bottom of the screen and are often used to present forms or other temporary content to the user. In this section, you'll take a look at the default modal presentation transition and how to customize it to suit your own needs.

The first thing you should do is create a view controller that will be presented modally. Start by creating a new Cocoa Touch Class and name it CustomPresentedViewController. Make sure that it subclasses UIViewController. After creating the file, open Main.storyboard and drag out a new UIViewController from the Object Library and set its class to CustomPresentedViewController in the Identity Inspector panel. Next, drag out a bar button item to the left side of the navigation bar on the contacts overview page. Set the bar button's label text to Show Modal.

Then press Ctrl, and drag from the bar button item to the new view controller to set up a segue from the contacts overview page to the view controller you just added. Select the Present Modally segue:

Finally, give the new view controller's view a bright blue background color, so it will be easier to see the transition later. If you run your app now, you can click on the Show Modal button and you'll see an empty view controller pop up from the bottom. You can't dismiss this view controller right now. That's okay; you will get to that later. Let's work on a custom transition to display this view controller first.

Custom view-controller transitions use several objects to facilitate the animation. The first object you will look at is transitioningDelegate for UIViewController. The transitioningDelegate property is responsible for providing an animation controller that provides the custom transition.

The animation controller uses a transitioning context object that provides information about the view controllers that are involved in the transition. Typically, these view controllers will be the current view controller and the view controller that is about to be presented.

The transitioning flow can be described in the following steps:

  1. A transition begins; the target view controller is asked for its transitioningDelegate.
  2. The transitioningDelegate is asked for an animation controller.
  3. The animation controller is asked for the animation duration.
  4. The animation controller is told to perform the animation.
  5. When the animation is complete, the animation controller calls completeTransition(_:) on the transitioning context to mark the animation as completed.

If step 1 or step 2 returns nil, or isn't implemented at all, the default animation for the transition is used. The objects involved in a custom transition are displayed in the following diagram:

Creating a separate object to control the animation is often a good idea because it allows you to reuse a transition and it keeps your code nice and clean. The animation controller should be an object that conforms to UIViewControllerAnimatedTransitioning. This object will take care of animating the presented view onto the screen. Let's create the animation controller object next.

Create a new Cocoa Touch class and name it CustomModalShowAnimator. Pick NSObject as its superclass. This class will act as the animation controller. After creating the new file, open it and add the following extension to make CustomModalShowAnimator conform to UIViewControllerAnimatedTransitioning:

extension CustomModalShowAnimator: UIViewControllerAnimatedTransitioning {

}

This makes the new class conform to the protocol that's required to be an animation controller. Xcode will show a build error because you haven't implemented all the methods to conform to UIViewControllerAnimatedTransitioning yet. Let's go over the methods one by one so you end up with a full implementation for the animation controller.

The first method that must be implemented for the animation controller is transitionDuration(using:). The implementation of this method is shown here:

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {   
    return 0.6   
} 

This method is used to determine the total transition duration in seconds. In this case, the implementation is simple  the animation should last 0.6 seconds.

The second method that needs to be implemented is animateTransition(using:). Its purpose is to take care of the actual animation for the custom transition. The implementation will take the target view controller and its view will be animated from the top of the screen downward to its final position. It will also do a little bit of scaling, and the opacity of the view will be animated; to do this, UIViewPropertyAnimator will be used. Add the following implementation to the animator:

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
  // 1
  guard let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)
    else { return }

  // 2
  let transitionContainer = transitionContext.containerView

  // 3
  var transform = CGAffineTransform.identity
  transform = transform.concatenating(CGAffineTransform(scaleX: 0.6,
                                                        y: 0.6))
  transform = transform.concatenating(CGAffineTransform(translationX:
    0, y: -200))

  toViewController.view.transform = transform
  toViewController.view.alpha = 0

  // 4
  transitionContainer.addSubview(toViewController.view)

  // 5
  let animationTiming = UISpringTimingParameters(
    dampingRatio: 0.8,
    initialVelocity: CGVector(dx: 1, dy: 0))

  let animator = UIViewPropertyAnimator(
    duration: transitionDuration(using: transitionContext),
    timingParameters: animationTiming)

  animator.addAnimations {
    toViewController.view.transform = CGAffineTransform.identity
    toViewController.view.alpha = 1
  }


  // 6
  animator.addCompletion { finished in
    transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
  }

  // 7
  animator.startAnimation()
} 

A lot is going on in the preceding code snippet. Let's go through the code step by step to see what's happening:

  1. The target view controller is extracted from the transition context. This allows you to use the view controller's view in the animation that you're about to perform.
  1. Obtain the animation's container view. The container view is a regular UIView and its intention is to contain all the animated views.
  1. Prepare the target-view controller's view for the animation. The view is transformed so it's off the screen and the transparency is set to make the view completely transparent.
  2. Once the view is prepared, it is added to the container view.
  3. The animations are set up and added to a property animator.
  4. The completion-handler for the property animator is configured so completeTransition(_:) is called on the context. The transitionWasCancelled variable is used to determine whether the animation completed normally.
  5. Start the property animator so the animations begin.

Now that the animation controller is complete, the UIViewControllerTransitioningDelegate protocol should be implemented on CustomPresentedViewController so it can act as its own transitioningDelegate. Open the file and add the following implementation code:

class CustomPresentedViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    transitioningDelegate = self
  }
}

extension CustomPresentedViewController: UIViewControllerTransitioningDelegate {

  func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return CustomModalShowAnimator()
  }

  func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    return nil
  }
}

This code adds conformance to the UIViewControllerTransitioningDelegate protocol and assigns the view controller as its own transitioning delegate. The animationController(forPresented:presenting:source:) method returns the animation controller you created before. The animationController(forDismissed:) method returns nil for now. Go ahead and test your custom transition! This is all the code required to create a custom display transition. Now that the app can display the modal view controller with a custom transition, let's add an interactive dismissal transition.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset