Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4370

Custom View Controller Presentation Transitions with Swift

$
0
0
Learn about iOS Animations in our Swift Spring Fling celebration!

Learn about iOS Animations in our Swift Spring Fling celebration!

Note from Ray: This is an abbreviated chapter from iOS Animations by Tutorials released as part of the Spring Swift Fling to give you a sneak peek of what’s inside the book. We hope you enjoy! :]

Whether you’re presenting the camera view controller, the address book, or one of your own custom-designed modal screens, you call the same UIKit method every time: presentViewController(_: animated:completion:). This method “gives up” the current screen to another view controller.

The default presentation animation simply slides the new view up to cover the current one. The illustration below shows a “New Contact” view controller sliding up over the list of contacts:

01_iAT_addressbook

In this tutorial, you’ll create your own custom presentation transitions controller to replace the default animations and liven up tutorial’s starter project.

Getting Started

Download the starter project: BeginnerCook-Starter, unarchive the zip file and open the Beginner Cook starter project. Select Main.storyboard to begin the tour:

image003

The first view controller (ViewController) contains the app’s title and main description as well as a scroll view at the bottom, which shows the list of available herbs.

The main view controller presents HerbDetailsViewController whenever the user taps one of the images in the list; this view controller sports a background, a title, a description and some buttons to credit the image owner.

There’s already enough code in ViewController.swift and HerbDetailsViewController.swift to support the basic application. Build and run the project to see how the app looks and feels:

image005

Tap on one of the herb images, and the details screen comes up via the standard vertical cover transition. That might be OK for your garden-variety app, but your herbs deserve better!

Your job is to add some custom presentation transitions to your app to make it blossom! You’ll replace the current stock animation with one that expands the tapped herb image to a full-screen view like so:

image007

Roll up your sleeves, put your developer apron on and get ready for the inner workings of custom presentation controllers!

Behind the Scenes of Custom Transitions

UIKit lets you customize your view controller’s presentation via the delegate pattern; you simply make your main view controller (or another class you create specifically for that purpose) adopt UIViewControllerTransitioningDelegate.

Every time you present a new view controller, UIKit asks its delegate whether or not it should use a custom transition. Here’s what the first step of the custom transitioning dance looks like:

image009

UIKit calls animationControllerForPresentedController(:_ presentingController:sourceController:); if that method returns nil UIKit uses the built-in transition. If UIKit receives an object instead, then UIKit uses that object as the animation controller for the transition.

There are a few more steps in the dance before UIKit can use the custom animation controller:

image011

UIKit first asks your animation controller (simply known as the animator) for the transition duration in seconds, then calls animateTransition() on it. This is when your custom animation gets to take center stage.

In animateTransition(), you have access to both the current view controller on the screen as well as the new view controller to be presented. You can fade, scale, rotate and manipulate the existing view and the new view however you like.

Now that you’ve learned a bit about how custom presentation controllers work, you can start to create your own.

Implementing Transition Delegates

Since the delegate’s task is to manage the animator object that performs the actual animations, you’ll first have to create a stub for the animator class before you can write the delegate code.

From Xcode’s main menu select File\New\File… and choose the template iOS\Source\Cocoa Touch Class.

Set the new class name to PopAnimator and make it a subclass of NSObject.

Open PopAnimator.swift and update the class definition to make it conform to the UIViewControllerAnimatedTransitioning protocol as follows:

class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {
 
}

You’ll see some complaints from Xcode since you haven’t implemented the required delegate methods yet, so you’ll stub those out next.

Add the following method to the class:

func transitionDuration(transitionContext: UIViewControllerContextTransitioning)-> NSTimeInterval {
  return 0
}

The 0 value above is just a placeholder value for the duration; you’ll replace this later with a real value as you work through the project.

Now add the following method stub to the class:

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
 
}

The above stub will hold your animation code; adding it should have cleared the remaining errors in Xcode.

Now that you have the basic animator class, you can move on to implementing the delegate methods on the view controller side.

Open ViewController.swift and add the following extension to the end of the file:

extension ViewController: UIViewControllerTransitioningDelegate{
 
}

This code indicates the view controller conforms to the transitioning delegate protocol. You’ll add some methods here in a moment.

Find didTapImageView() in the main body of the class. Near the bottom of that method you’ll see the code that presents the details view controller. herbDetails is the instance of the new view controller; you’ll need to set its transitioning delegate to the main controller.

Add the following code right before the last line of the method that calls presentViewController:

herbDetails.transitioningDelegate = self

Now UIKit will ask ViewController for an animator object every time you present the details view controller on the screen. However, you still haven’t implemented any of UIViewControllerTransitioningDelegate’s methods, so UIKit will still use the default transition.

The next step is to actually create your animator object and return it to UIKit when requested.

Add the following new property to ViewController:

let transition = PopAnimator()

This is the instance of PopAnimator that will drive your animated view controller transitions. You only need one instance of PopAnimator since you can continue to use the same object each time you present a view controller, as the transitions are the same every time.

Now add the first delegate method to the extension in ViewController:

func animationControllerForPresentedController(
 presented: UIViewController!,
 presentingController presenting: UIViewController!,
 sourceController source: UIViewController!) ->
 UIViewControllerAnimatedTransitioning! {
 
  return transition
}

This method takes a few parameters that let you make an informed decision whether or not you want to return a custom animation. In this tutorial you’ll always return your single instance of PopAnimator since you have only one presentation transition.

You’ve already added the delegate method for presenting view controllers, but how will you deal with dismissing one?

Add the following delegate method to handle this:

func animationControllerForDismissedController(dismissed: UIViewController!) -> UIViewControllerAnimatedTransitioning! {
  return nil
}

The above method does essentially the same thing as the previous one: you check which view controller was dismissed and decide whether to return nil and use the default animation, or return a custom transition animator and use that instead. At the moment you return nil, as you aren’t going to implement the dismissal animation until later.

You finally have a custom animator to take care of your custom transitions. But does it work?

Build and run your project and tap one of the herb images:

image013

Nothing happens. Why? You have a custom animator to drive the transition, but… oh, wait, you haven’t added any code to the animator class! :] You’ll take care of that in the next section.

Creating your Transition Animator

Open PopAnimator.swift; this is where you’ll add the code to transition between the two view controllers.

First, add the following properties to this class:

let duration    = 1.0
var presenting  = true
var originFrame = CGRect.zeroRect

You’ll use duration in several places, such as when you tell UIKit how long the transition will take and when you create the constituent animations.

You also define presenting to tell the animator class whether you are presenting or dismissing a view controller. You want to keep track of this because typically, you’ll run the animation forward to present and in reverse to dismiss.

Finally, originFrame represents the image view the user originally tapped. You’re going to animate out from the herb image in the scroll view, so this is will be the starting frame.

Now you can move on to the UIViewControllerAnimatedTransitioning methods.

Replace the code inside transitionDuration() with the following:

return duration

Reusing the duration property lets you easily experiment with the transition animation; you can simply modify the value of the property to make the transition run faster or slower.

Setting your Transition’s Context

It’s time to add some magic to animateTransition. This method has one parameter of type UIViewControllerContextTransitioning, which gives you access to the parameters and view controllers of the transition.

Before you start working on the code itself, it’s important to understand what the animation context actually is.

When the transition between the two view controllers begins, the existing view is added to a transition container view and the new view controller’s view is created but not yet visible, as illustrated below:

image015

Therefore your task is to add the new view to the transition container within animateTransition(), “animate in” its appearance, and “animate out” the old view if required.

By default, the old view is removed from the transition container when the transition animation is done:

image017

Adding a Pop Transition

To create the animations for your custom transition you need to work with the so called “container” view of the transition context. This is a temporary view (much like a scratchpad) that gets added to the screen only for the time while the transition takes place. You will create all your animations in this view.

Insert into animateTransition():

let containerView = transitionContext.containerView()
 
let toView =
  transitionContext.viewForKey(UITransitionContextToViewKey)!
 
let herbView = presenting ? toView : transitionContext.viewForKey(UITransitionContextFromViewKey)!

containerView is where your animations will live, while toView is the new view to present. If you’re presenting, herbView is just the toView; otherwise it will be fetched from the context. For both presenting and dismissing, herbView will always be the view that you animate.

When you present the details controller view, it will grow to take up the entire screen. When dismissed, it will shrink to the image’s original frame.

Add the following to animateTransition():

let initialFrame = presenting ? originFrame : herbView.frame
let finalFrame = presenting ? herbView.frame : originFrame
 
let xScaleFactor = presenting ?
  initialFrame.width / finalFrame.width :
  finalFrame.width / initialFrame.width
 
let yScaleFactor = presenting ?
  initialFrame.height / finalFrame.height :
  finalFrame.height / initialFrame.height

In the code above, you detect the initial and final animation frames and then calculate the scale factor you need to apply on each axis as you animate between each view.

Now you need to carefully position the new view so it appears exactly above the tapped image; this will make it look like the tapped image expands to fill the screen.

Add the following to animateTransition():

let scaleTransform = CGAffineTransformMakeScale(xScaleFactor, yScaleFactor)
 
if presenting {
  herbView.transform = scaleTransform
  herbView.center = CGPoint(
    x: CGRectGetMidX(initialFrame),
    y: CGRectGetMidY(initialFrame))
  herbView.clipsToBounds = true
}

When presenting the new view, you set its scale and position so it exactly matches the size and location of the initial frame.

Now add the final bits of code to animateTransition():

containerView.addSubview(toView)
containerView.bringSubviewToFront(herbView)
 
UIView.animateWithDuration(duration, delay:0.0,
  usingSpringWithDamping: 0.4,
  initialSpringVelocity: 0.0,
  options: nil,
  animations: {
    herbView.transform = self.presenting ?
     CGAffineTransformIdentity : scaleTransform
 
    herbView.center = CGPoint(x: CGRectGetMidX(finalFrame),
                              y: CGRectGetMidY(finalFrame))
 
  }, completion:{_ in
    transitionContext.completeTransition(true)
})

This will first add toView to the container. Next, you need to make sure the herbView is on top since that’s the only view you’re animating. Remember that when dismissing, toView is the original view so in the first line, you’ll be adding it on top of everything else and your animation will be hidden away unless you bring herbView to the front.

Then, you can kick off the animations – using a spring animation here will give it a bit of bounce.

Inside the animations expression, you change the transform and position of herbView. When presenting, you’re going from the small size at the bottom to the full screen so the target transform is just the identity transform. When dismissing, you animate it to scale down to match the original image size.

At this point, you’ve set the stage by positioning the new view controller over the tapped image; you’ve animated between the initial and final frames; and finally, you’ve called completeTransition() to hand things back to UIKit. It’s time to see your code in action!

Build and run your project; tap the first herb image to see your view controller transition in action:

image019

Currently your animation starts from the top-left corner; that’s because the default value of originFrame has the origin at (0, 0) – and you never set it to any other value.

Open ViewController.swift and add the following code to the top of animationControllerForPresentedController():

transition.originFrame = 
  selectedImage!.superview!.convertRect(selectedImage!.frame, toView: nil)
 
transition.presenting = true

This sets the originFrame of the transition to the frame of selectedImage, which is the image view you last tapped. Then you set presenting to true and hide the tapped image during the animation.

Build and run your project again; tap different herbs in the list and see how your transition looks for each:

image023

Adding a Dismiss Transition

All that’s left to do is dismiss the details controller. You’ve actually done most of the work in the animator already – the transition animation code does the logic juggling to set the proper initial and final frames, so you’re most of the way to playing the animation both forwards and backwards. Sweet! :]

Open ViewController.swift and replace the body of animationControllerForDismissedController() with the following:

transition.presenting = false
return transition

This tells your animator object that you’re dismissing a view controller so the animation code will run in the correct direction.

Build and run the project to see the result. Tap on an herb and then tap anywhere on screen to dismiss it and enjoy!

image027

Where To Go From Here?

You can grab the completed project from this tutorial here: BeginnerCook-Completed.

From here, you can do a lot more to polish this transition even further. For example, here are some ideas to develop:

  • hide tapped images during transitions so it really looks like they grow to take up the full screen
  • fade in and out each herb’s description text so the transition animation is smoother
  • animate the corner radius of the selected images
  • test and adjust the transition for landscape orientation

All of these and more are tackled in the presentation transition animations chapter in iOS Animations by Tutorials. In the book, you’ll learn in a bit more detail about view controller presentation transitions, orientation change animations, navigation controllers and interactive transitions, and more.

We hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!

Custom View Controller Presentation Transitions with Swift is a post from: Ray Wenderlich

The post Custom View Controller Presentation Transitions with Swift appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4370

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>