Easy interactive interruptible custom ViewController transitions

Overview
Transition

CocoaPods version Carthage compatible license platform

Introduction

Transition is a library that helps you build iOS view controller transitions. Implementing a nice interactive custom view controller transition involves quite a number of components. You have to implement the correct delegates, handle the switching between passive animation and active interaction phases, ensure the timing is right, think of interruption and cancellation, keep responsibilities separated... It quickly gets messy! This is where Transition helps you out: you just define the animation and the interaction, Transition ties it all together.

In short:

  • You specify single-responsibility components (animation, interaction, ...)
  • Transition ties them together

Examples

There are several examples (which can be found in Examples/):

  1. SimpleExample: implements the basic steps explained in this README.
  2. TabBarTransitionsExample: shows you how to implement custom UITabBarController transition animations with custom interaction.
  3. ModalTransitionsExample: shows you how to implement custom modal transition animations that include interaction with a shared element.
  4. BuiltInTransitionsCatalog: shows a small collection of built-in transition animations.

To run an example project, clone the repo, navigate to one of these example directories, and run pod install from that directory first.



Requirements

  • iOS 10.0+
  • Swift 3.0+

Usage

1. The AnimationLayer

The AnimationLayer is the most essential part of setting up a transition; without it, there'll be no animation. An AnimationLayer is a simple struct that takes two arguments:

1. Animation function

This is a simple closure with the signature () -> Void. In this closure you define your animation, just as you would with a UIView or UIViewPropertyAnimator animation. For each AnimationLayer, Transition will instantiate a UIViewPropertyAnimator, passing it your animation block.

2. TimingParameters

This defines the timing of your animation. It must be a UITimingCurveProvider, such as an instance of UICubicTimingParameters or UISpringTimingParameters.

(3. AnimationRange)

Additionally, you can set an AnimationRange, which by default is set to AnimationRange.full. This range defines the start and end point (as fractions of the total transition animation duration) between which your AnimationLayer's animation will run.

You create your AnimationLayer as follows:

let animationLayer = AnimationLayer(timingParameters: UICubicTimingParameters(animationCurve: .easeOut)
                                           animation: { topView?.transform = targetTransform })

😦 ... Hey, wait! Where do that topView and targetTransform come from?

2. The TransitionAnimation

Your AnimationLayer is defined as a part of your TransitionAnimation. This represents all (non-interactive) animation during a transition. The TransitionAnimation protocol exposes an array of AnimationLayers. Additionally it contains two functions; one for setup and one for completion. Before starting animation, your setup function will be called, passing you the transitioningContext that among others contains the fromView and toView in the transition. The completion function is called when the entire transition completes, allowing you to clean up any temporary views added in the setup.

class SimpleAnimation : TransitionAnimation {
    
    private weak var topView: UIView?
    private var targetTransform: CGAffineTransform = .identity
    
    func setup(in operationContext: TransitionOperationContext) {
        let context = operationContext.context
        let isPresenting = operationContext.operation.isPresenting
        
        //  We have to add the toView to the transitionContext, at the appropriate index:
        if isPresenting {
            context.containerView.addSubview(context.toView)
        } else {
            context.containerView.insertSubview(context.toView, at: 0)
        }
        context.toView.frame = context.finalFrame(for: context.toViewController)
        
        //  We only animate the view that will be on top:
        topView = isPresenting ? context.toView : context.fromView
        
        let hiddenTransform = CGAffineTransform(translationX: 0, y: -context.containerView.bounds.height)
        
        topView?.transform = isPresenting ? hiddenTransform : .identity
        targetTransform = isPresenting ? .identity : hiddenTransform
    }
    
    var layers: [AnimationLayer] {
        return [AnimationLayer(timingParameters: AnimationTimingParameters(animationCurve: .easeOut), animation: animate)]
    }
    
    func animate() {
        topView?.transform = targetTransform
    }
    
    func completion(position: UIViewAnimatingPosition) {}
}

🤔 ... But what about duration?

3. The Transition

You just defined the animation of your transition. You now create a Transition struct that has an animation part (your TransitionAnimation) and an optional sharedElement part, which you can see implemented in the Modal and Navigation examples. And the Transition has a duration!

let transition = Transition(duration: 2.0, animation: SimpleAnimation())

😬 ... And where do I put that transition?

4. The TransitionController

Almost there. Say you want to use this transition for UINavigationController transitions. Let's make a convenience object that isolates transition-related functionality for a navigationController:

class MyNavigationTransitions {
    let transitionController: TransitionController
    let transitionsSource = MyNavigationTransitionSource()
    
    init(navigationController: UINavigationController) {
        transitionController = TransitionController(forTransitionsIn: navigationController, transitionsSource: transitionsSource)
    }
}

class MyNavigationTransitionSource : TransitionsSource {
    func transitionFor(operationContext: TransitionOperationContext, interactionController: TransitionInteractionController?) -> Transition {
        return Transition(duration: 0.5, animation: SimpleAnimation())
    }
}

The TransitionController takes responsibility for animating transitions in the given navigationController, using an external TransitionsSource to provide operation-specific transitions (the operation – in this case push or pop – can be obtained from the TransitionOperationContext). This means you can return different transition animations for push and pop. You can also provide a single animation that behaves differently depending on the operation (see the SimpleAnimation).

Now, initialize your MyNavigationTransitions, passing it your navigationController.

🤓 ... Is that it?

Yes!

😎

At least, for custom view controller transition animations. There's a lot more that'll help you set up a custom interaction gesture and a shared element that can move between the transitioning views.

The above steps are implemented in the SimpleExample that can be found in the Examples/ directory.

Further reading

Installation

Transition is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Transition'

Your input is welcome!

If you have any suggestions, please get in touch with us. Feel free to fork and submit pull requests. Also, we're Dutch, so if any naming is odd, might be improved or is just plain inappropriate, let us know!

Backlog

License

Transition is available under the MIT license. See the LICENSE file for more info.

Comments
  • Provided examples contain bugs/glitches

    Provided examples contain bugs/glitches

    When I start interactive unwinding and cancel it by releasing my finger before threshold is passed, transition becomes broken. Please see following video explanations: https://www.dropbox.com/s/v6vb2gczopew1sw/Transition.mov

    opened by dkarbayev 4
  • Interactive Modal using Pan crashes on Dismiss

    Interactive Modal using Pan crashes on Dismiss

    Hi,

    Great library. I'm trying to do a simple interactive modal transition. I'm using PanInteractionController(forModalTransitionsAtEdge...) and the TransitionController(forInteractiveModalPresentationsFrom...) to setup the interactionController and transitionController. Presenting works fine.

    When I try to dismiss the view by flicking upwards, I get "fatal error: unexpectedly found nil while unwrapping an Optional value" in "UIViewControllerContextTransitioning+Properties.swift". Basically "toView" is nil here:

    case .dismiss: if toView.superview == nil { containerView.insertSubview(toView, belowSubview: fromView) }

    Is this an issue, or am I not setting up the transition controller correctly.

    Thanks. Jan-Michael

    PS: Here's the code to produce the issue. The StickerViewController is just a view with a UILabel.

    import UIKit import Transition

    class ViewController: UIViewController { private(set) var transitionController: TransitionController! private(set) var interactionController: PanInteractionController!

    fileprivate weak var stickerViewController: StickerViewController?
    
    override func viewDidLoad()
    {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.red
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        if interactionController == nil
        {
            self.interactionController = PanInteractionController(forModalTransitionsAtEdge: .top)
            self.transitionController = TransitionController(forInteractiveModalPresentationsFrom: self, transitionsSource: self, interactionController: interactionController)
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    

    }

    extension ViewController : TransitionsSource { // proivide transition for presentation / dismissal func transitionFor(operationContext: TransitionOperationContext, interactionController: TransitionInteractionController?) -> Transition { //animation let transitionAnimation = MoveTransitionAnimation(forModalTransitionsAtEdge: .top) let transition = Transition(duration: 0.4, animation: transitionAnimation) return transition } }

    extension ViewController : InteractiveModalTransitionOperationDelegate { func viewControllerForInteractiveModalPresentation(by sourceViewController: UIViewController, gestureRecognizer: UIGestureRecognizer) -> UIViewController { let vc = self.storyboard?.instantiateViewController(withIdentifier: "sticker") vc?.modalPresentationStyle = .overCurrentContext vc?.transitioningDelegate = self as? UIViewControllerTransitioningDelegate

        self.stickerViewController = vc as? StickerViewController
        return vc!
    }
    

    }

    opened by jtressle 4
  • Swift 4.2

    Swift 4.2

    This project uses many methods deprecated in Swift 4.2. Right now, it is possible to build it with Swift 4.0 and include it in a project build with Swift 4.2. However, it would be nice to have an update to the project itself to work with Swift 4.2.

    opened by netspencer 3
  • Flick to dismiss causes animations to break

    Flick to dismiss causes animations to break

    Flicking to dismiss view controllers breaks the animations. You can get stuck between two states. I was able to repro using Modal Transition example and Pinterest example easily.

    • Modal Example: Flick the icon up; notice animation is stuck.
    • Pinterest Example: Dismiss a view controller by flicking down. Again animation gets stuck.
    • I can provide video if you are not able to repro it. Other than this issue; it looks great; Awesome job by the team! keep it up!!
    opened by aveace 3
  • iPhone X Safe Margins

    iPhone X Safe Margins

    After clicking on a cell, the expansion animation seems like it's not taking the safety margins into account.

    Before Autolayout Breaks Constraints

    simulator screen shot - iphone x - 2018-10-14 at 22 12 54

    After Autolayout Breaks Constraints

    simulator screen shot - iphone x - 2018-10-14 at 22 12 58

    Here is the error log for your reference :D

    2018-10-14 22:11:19.430659-0500 Transition_Example[48438:2420401] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    	Probably at least one of the constraints in the following list is one you don't want. 
    	Try this: 
    		(1) look at each constraint and try to figure out which you don't expect; 
    		(2) find the code that added the unwanted constraint or constraints and fix it. 
    (
        "<NSLayoutConstraint:0x600002cb21c0 Transition_Example.CollectionViewCell:0x7fd929c67b50.height == 399.967   (active)>",
        "<NSLayoutConstraint:0x600002cb2580 'UIView-Encapsulated-Layout-Height' Transition_Example.CollectionViewCell:0x7fd929c67b50.height == 400   (active)>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x600002cb2580 'UIView-Encapsulated-Layout-Height' Transition_Example.CollectionViewCell:0x7fd929c67b50.height == 400   (active)>
    
    Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
    The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
    2018-10-14 22:11:19.488227-0500 Transition_Example[48438:2420401] [LayoutConstraints] Unable to simultaneously satisfy constraints.
    	Probably at least one of the constraints in the following list is one you don't want. 
    	Try this: 
    		(1) look at each constraint and try to figure out which you don't expect; 
    		(2) find the code that added the unwanted constraint or constraints and fix it. 
    (
        "<NSLayoutConstraint:0x600002cb21c0 Transition_Example.CollectionViewCell:0x7fd929c67b50.height == 399.967   (active)>",
        "<NSLayoutConstraint:0x600002cb2580 'UIView-Encapsulated-Layout-Height' Transition_Example.CollectionViewCell:0x7fd929c67b50.height == 400   (active)>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x600002cb2580 'UIView-Encapsulated-Layout-Height' Transition_Example.CollectionViewCell:0x7fd929c67b50.height == 400   (active)>
    
    Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
    The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
    
    opened by mkchoi212 0
  • Is example working as expected?

    Is example working as expected?

    In the README file there is a beautiful demo of transition: https://github.com/Touchwonders/Transition/raw/master/Documentation/artwork/navigation.gif

    An example app called "Navigation" has bit similar push transition, however it has a default ios pop transition. Is it intentional that the pop transition is default or Transition package does not work expected in iOS 14?

    Would you mind elaborating how to accomplish a transition like in README demo?

    opened by AugustDev 1
Releases(1.3.0)
Owner
Touchwonders
We create apps that make you smile :)
Touchwonders
A simple way to create custom interactive UIViewController transitions

EasyTransitions is a library that helps developers create custom interactive transitions using simple functions defined in a protocol and avoid handli

Marcos Griselli 1.6k Jan 1, 2023
SamuraiTransition is an open source Swift based library providing a collection of ViewController transitions featuring a number of neat “cutting” animations.

SamuraiTransiton is a ViewController transition framework in Swift. It is an animation as if Samurai cut out the screen with a sword. transition types

hachinobu 273 Dec 29, 2022
Custom interactive transition like Apple Music iOS App (iOS 9). written in Swift.

MusicPlayerTransition Custom interactive transition like Apple Music iOS App. written in Swift. Demo See demo on Appetize.io Using Transition Animator

Airin 642 Nov 17, 2022
Library for smooth animation of images during transitions.

ImageTransition ImageTransition is a library for smooth animation of images during transitions. Something looks like below: e.g. UIImageView e.g. UIIm

shtnkgm 207 Dec 3, 2022
Allows trendy transitions using swipe gesture such as "swipe back anywhere".

SwipeTransition allows trendy transitions using swipe gesture such as "swipe back". Try the demo on the web (appetize.io): https://appetize.io/app/peb

Tatsuya Tanaka 294 Dec 27, 2022
Painless custom transitioning. Easy extend, easy setup, just focus on animations.

TransitionManager Painless custom transitioning. Easy extend, easy setup, just focus on animations. Installation CocoaPods You can use CocoaPods to in

Cem Olcay 205 Aug 23, 2022
Custom-Transition - A repo about custom transition between two view controllers

Custom-Transition in SWIFT This is a repo about custom transition between two vi

Prakash Chandra Awal 0 Jan 6, 2022
A custom modal transition that presents and dismiss a controller with an expanding bubble effect.

A custom modal transition that presents and dismiss a controller inside an expanding and shrinking bubble. Screenshot Usage Install through CocoaPods:

Andrea Mazzini 3.3k Dec 28, 2022
SPLarkController - Custom transition between controllers. Settings controller for your iOS app.

SPLarkController About Transition between controllers to top. You can change animatable height after presentation controller. For presentation and dis

Ivan Vorobei 965 Dec 17, 2022
🌊 - Jelly is a library for animated, non-interactive & interactive viewcontroller transitions and presentations with the focus on a simple and yet flexible API.

Jelly is a library for animated, non-interactive & interactive viewcontroller transitions and presentations with the focus on a simple and yet flexibl

Sebastian Boldt 2.4k Dec 25, 2022
🌊 - Jelly is a library for animated, non-interactive & interactive viewcontroller transitions and presentations with the focus on a simple and yet flexible API.

Jelly is a library for animated, non-interactive & interactive viewcontroller transitions and presentations with the focus on a simple and yet flexibl

Sebastian Boldt 2.4k Dec 25, 2022
A simple way to create custom interactive UIViewController transitions

EasyTransitions is a library that helps developers create custom interactive transitions using simple functions defined in a protocol and avoid handli

Marcos Griselli 1.6k Jan 1, 2023
Wave is a spring-based animation engine for iOS that makes it easy to create fluid, interruptible animations that feel great.

Wave is a spring-based animation engine for iOS and iPadOS. It makes it easy to create fluid, interactive, and interruptible animations that feel great.

Janum Trivedi 1.2k Jan 8, 2023
AlertTransition is a extensible library for making view controller transitions, especially for alert transitions.

AlertTransition AlertTransition is a extensible library for making view controller transitions, especially for alert transitions. Overview AlertTransi

Loopeer 570 Nov 29, 2022
SamuraiTransition is an open source Swift based library providing a collection of ViewController transitions featuring a number of neat “cutting” animations.

SamuraiTransiton is a ViewController transition framework in Swift. It is an animation as if Samurai cut out the screen with a sword. transition types

hachinobu 273 Dec 29, 2022
SamuraiTransition is an open source Swift based library providing a collection of ViewController transitions featuring a number of neat “cutting” animations.

SamuraiTransiton is a ViewController transition framework in Swift. It is an animation as if Samurai cut out the screen with a sword. transition types

hachinobu 273 Dec 29, 2022
Present a sheet ViewController easily and control ViewController height with pangesture

PanControllerHeight is designed to present a sheet ViewController easily and control ViewController height with pangesture.

null 2 May 3, 2022
Allows a swipe on any part of the screen to start an interruptible pop animation to the previous view

Lazy Pop SwiftUI Swiping on any part of the screen starts an interruptible pop animation to the previous view. Forked from https://github.com/rishi420

Joe Hinkle 150 Dec 18, 2022
A UICollectionViewLayout subclass that adds custom transitions/animations to the UICollectionView without effecting your existing code.

AnimatedCollectionViewLayout Normally a UICollectionView has no transition effects when you scroll from one item to another. There are lots of ways to

Jin Wang 4.5k Jan 1, 2023
A simple iOS app with one default and four custom transitions.

A simple iOS app with one default and four custom transitions. The app uses the same two view controllers for every transition.

coding_o 7 Aug 18, 2021