CSCardTransition is a small library allowing you to create wonderful push
and pop
transition animations like in the App Store.
It works side by side with your navigation controller, and ensure that you only code what's necessary for your custom transition.
Library Example | On Time Available for iOS |
My Toolbox Available for iOS and MacOS |
Add this line to your Podfile.
pod 'CSCardTransition'
Drag the files inside the CSCardTransition
folder into your project.
You can go through the example provided in this repo, and run it, to see how to implement beautiful transitions. You can also follow the following steps.
After creating a "Card Presenter" View Controller and your "Presented Card" View Controller, you will need to follow this steps:
Add the 4 following lines to your custom UINavigationController.
import UIKit
// 1
import CSCardTransition
class YourNavigationController: UINavigationController {
...
override func viewDidLoad() {
super.viewDidLoad()
// 2
delegate = self
}
...
}
extension YourNavigationController: UINavigationControllerDelegate {
func navigationController(
_ navigationController: UINavigationController,
animationControllerFor operation: UINavigationController.Operation,
from fromVC: UIViewController,
to toVC: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
// 3
return CSCardTransition.navigationController(
navigationController,
animationControllerFor: operation,
from: fromVC,
to: toVC
)
}
func navigationController(
_ navigationController: UINavigationController,
interactionControllerFor animationController: UIViewControllerAnimatedTransitioning
) -> UIViewControllerInteractiveTransitioning? {
// 4
return CSCardTransition.navigationController(
navigationController,
interactionControllerFor: animationController
)
}
}
In the View Controller that serves as the view "Presenter", in other word, in the View Controller that contains the card to be expanded, you will need to add the CSCardViewPresenter
protocol and define what UIView
should be used as the card to be presented:
extension YourViewController: CSCardViewPresenter {
var cardViewPresenterCard: UIView? {
return cardView // Return the view that represents the start of your transition
}
}
In the View Controller that serves as the "Presented" card view, in other word, in the View Controller that is the expanded Card View, you will need to define a CSCardTransitionInteractor
and add the CSCardPresentedView
protocol:
class YourViewController: UIViewController {
...
// To enable the drag gesture to pop out the card view and go back to the parent view controller.
lazy var cardTransitionInteractor: CSCardTransitionInteractor? = CSCardTransitionInteractor(viewController: self)
...
}
extension YourViewController: CSCardPresentedView {
... // You will probably add certain methods here later.
}
If your status bar style is different from one view to the other, you will need to implement the cardViewPresenterShouldUpdateBar(to style: UIStatusBarStyle)
protocol in the Card Presenter View Controller and in the Presented Card View Controller so that the transition stays smooth. To do so, you may want to add a new var that determines the current status bar style like in the following example:
class YourViewController: UIViewController {
...
// /!\ Set the following var to the style of the status bar in you view controller (here: .default)
private var currentStatusBarStyle: UIStatusBarStyle = .default
override var preferredStatusBarStyle: UIStatusBarStyle { currentStatusBarStyle } // Overrides the status bar style
...
}
extension YourViewController: CSCardViewPresenter // Do the same for the CSCardPresentedView {
...
/// Called when the status bar style should be updated to match the transition progress
func cardViewPresenterShouldUpdateBar(to style: UIStatusBarStyle) {
currentStatusBarStyle = style
setNeedsStatusBarAppearanceUpdate() // Tell the OS to change the status bar style
}
}
You may want to customize you transition by disabling/enabling/updating layout constraints, changing view's alpha, corner radius, ... You may also want to disable the card transition at some point for some reason (for example when you don't know if the card view is still on screen in the Parent View Controller). You can do so by implementing the following methods. You can read the code example to get some ideas.
extension YourViewController: CSCardPresentedView {
/// A Boolean indicating whether or not the card transition should occur.
var cardTransitionEnabled: Bool { get }
/// Called when the transition to this view controller is about to start.
/// - Parameter cardView: The UIView used in the parent view controller to start the transition.
func cardPresentedViewWillStartPresenting(from cardView: UIView)
/// Called when the transition to this view controller just started.
/// Autolayout changes will automatically be animated here.
func cardPresentedViewDidStartPresenting()
/// Called when the transition to this view controller is currently in progress
/// - Parameter progress: The current progress of the transition (between 0 and 1)
func cardPresentedViewDidUpdatePresentingTransition(progress: CGFloat) {}
/// Called when the transition to this view controller is about to end.
func cardPresentedViewWillEndPresenting() {}
/// Called when the transition back to the parent view controller is about to start.
func cardPresentedViewWillStartDismissing() {}
/// Called when the transition back to the parent view controller has started.
func cardPresentedViewDidStartDismissing() {}
/// Called when the transition back to the parent view controller is about to be canceled.
func cardPresentedViewWillCancelDismissing() {}
/// Called when the transition back to the parent view controller is currently in progress.
/// - Parameter progress: The current progress of the transition (between 0 and 1)
func cardPresentedViewDidUpdateDismissingTransition(progress: CGFloat) {}
/// Called when the transition back to the parent view controller is about to be completed.
func cardPresentedViewWillEndDismissing() {}
}
You may want to customize you transition in the presenter by disabling/enabling layout constraints, changing view's alpha, corner radius, ... You can do so by implementing the following methods. You can read the code example to get some ideas.
extension YourViewController: CSCardViewPresenter {
/// Called when the transition to the card view controller just started.
/// Autolayout changes will automatically be animated here.
func cardViewPresenterDidStartDismissing() {}
/// Called when the transition to the card view controller is currently in progress
/// - Parameter progress: The current progress of the transition (between 0 and 1)
func cardViewPresenterDidUpdateDismissingTransition(progress: CGFloat) {}
/// Called when the transition to the card view controller is about to end.
func cardViewPresenterWillEndDismissing() {}
/// Called when the transition back to this view controller is about to start.
func cardViewPresenterWillStartPresenting() {}
/// Called when the transition back to this view controller has started.
func cardViewPresenterDidStartPresenting() {}
/// Called when the transition back to this view controller is about to be canceled.
func cardViewPresenterWillCancelPresenting() {}
/// Called when the transition back to this view controller is currently in progress.
/// - Parameter progress: The current progress of the transition (between 0 and 1)
func cardViewPresenterDidUpdatePresentingTransition(progress: CGFloat) {}
/// Called when the transition back to this view controller is about to be completed.
func cardViewPresenterWillEndPresenting() {}
}
You can customize all of the properties below simply by providing a custom instance of CSCardTransitionProperties to the Presented View Controller. For instance:
extension YourViewController: CSCardPresentedView {
var cardTransitionProperties: CSCardTransitionProperties {
return CSCardTransitionProperties(
/// Presenting animation properties
presentPositioningDuration: TimeInterval,
presentResizingDuration: TimeInterval,
presentStatusStyleUpdateDuration: TimeInterval,
/// Dismissing animation properties
dismissPositioningDuration: TimeInterval,
dismissResizingDuration: TimeInterval,
dismissBlurDuration: TimeInterval,
dismissStatusStyleUpdateDuration: TimeInterval,
/// Fade transition duration between presented card view and presenter card view
dismissFadeCardAnimationTime: TimeInterval,
/// How far should the user swipe to dismiss the view
preDismissingTransitionProgressPortion: CGFloat,
/// Cancel animation duration
cancelTransitionResizingDuration: TimeInterval,
/// Blurred background color during transition
transitionBackgroundColor: UIColor
)
}
}
Note: all of these properties have default values, so you can skip the ones you don't want to change in the instance creation.
The transition must be quick in production, but slow it down (to 1/10) during its development so you can easily see what is working and what still needs some improvements. You can enable debug mode by simply providing a debug instance of the CSCardTransitionProperties to the Presented View Controller.
extension YourViewController: CSCardPresentedView {
var cardTransitionProperties: CSCardTransitionProperties {
return .debug
}
}
A CSCardPresentedView
can also be a CSCardViewPresenter
, that is what makes this library so powerful 😉
This library is brought to you by Creastel, French Digital Agency. You can reach us at hello@creastel.com.