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.

Issues
  • Pinterest example

    Pinterest example

    Hi, where can I find the demo project with the pinterest transition? In the readme there is the gif, but I didn't found it in the example directory.

    opened by taglia3 7
  • 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
  • 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
  • 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
  • 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
  • Add Swift 4 Support

    Add Swift 4 Support

    Updating code to support Swift 4.

    opened by chrisiKern 2
  • Fix code indentation in Readme

    Fix code indentation in Readme

    opened by BasThomas 1
  • Fix examples

    Fix examples

    This PR fix Example Apps that need pod install.

    opened by colorbox 1
  • Swift 4

    Swift 4

    Hi,

    Any news on moving officially to 4+ ?

    Regards, H.

    opened by hernangonzalez 1
  • 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 10, 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 270 Dec 1, 2021
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 640 Dec 29, 2021
Protocol to handle initial Loadings, Empty Views and Error Handling in a ViewController & views

StatusProvider Protocol to handle initial Loadings, Empty Views and Error Handling in a ViewController & views CocoaPods Podfile pod 'StatusProvider'

Mario Hahn 887 Dec 27, 2021
FSPagerView is an elegant Screen Slide Library. It is extremely helpful for making Banner View、Product Show、Welcome/Guide Pages、Screen/ViewController Sliders.

SWIFT OBJECTIVE-C FSPagerView is an elegant Screen Slide Library implemented primarily with UICollectionView. It is extremely helpful for making Banne

Wenchao Ding 6.3k Jan 5, 2022
You can dismiss modal viewcontroller like Facebook Messenger by pulling scrollview or navigationbar in Swift.

PullToDismiss PullToDismiss provides dismiss modal viewcontroller function like Facebook Messenger by pulling scrollview or navigationbar with smooth

Suguru Kishimoto 473 Jan 6, 2022
Swipable tab and menu View and ViewController.

SwipeMenuViewController Overview SwipeMenuViewController provides SwipeMenuView and SwipeMenuViewController. This is very useful to build swipe-based

Yusuke Morishita 1.1k Jan 5, 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 198 Nov 6, 2021
iOS Interactive Side Menu written in Swift.

Interactive Side Menu A customizable, interactive, auto expanding and collapsing side menu for iOS written in Swift. Here are some of the ways Interac

Handsome 708 Dec 31, 2021
A UINavigationController subclass that support pop interactive UINavigationbar with hidden or show.

KDInteractiveNavigationController Features ✨ UINavigationController interactive with UINavigationBar hidden or show Hide all UINavigationController ba

Kingiol 153 Dec 13, 2020
™️ A powerful paging view controller with interactive indicator bars

⭐️ Features Easy to implement page view controller with interactive indicator bars. Highly adaptable and powerful customization. Fully extensible with

UI At Six 2.2k Jan 10, 2022
Interactive view transition to display menus with flowing and bouncing effects in Swift

FlowingMenu FlowingMenu provides an interactive transition manager to display menu with a flowing and bouncing effects. The Objective-C countepart is

Yannick Loriot 962 Jan 4, 2022
Swipe between pages with an interactive title navigation control. Configure horizontal or vertical chains for unlimited pages amount.

SlideController is a simple and flexible UI component fully written in Swift. Built using power of generic types, it is a nice alternative to UIPageVi

Touchlane 382 Dec 29, 2021
A navigation controller that displays its view controllers as an interactive stack of cards.

CardNavigation The easiest way to turn a navigation controller into an interactive stack of cards. Highlights ✅ Fully interactive and interruptible ✅

James Randolph 32 Oct 9, 2021
AdaptiveController is a 'Progressive Reduction' Swift UI module for adding custom states to Native or Custom iOS UI elements. Swift UI component by @Ramotion

ADAPTIVE TAB BAR 'Progressive Reduction' module for adding custom states to Native or Custom UI elements. We specialize in the designing and coding of

Ramotion 2k Nov 30, 2021
Kit for building custom gauges + easy reproducible Apple's style ring gauges.

GaugeKit ##Kit for building custom gauges + easy reproducible Apple's style ring gauges. -> Example Usage Open GaugeKit.xcworkspace and change the sch

Petr Korolev 1k Dec 28, 2021
Custom UIKit control to select a distance with a pan gesture, written in Swift

Distance Picker DistancePicker is a custom UIKit control to select a distance with a pan gesture. It looks like a ruler with multiple distance marks a

Quentin Mathé 118 Oct 28, 2021
iOS custom controller used in Jobandtalent app to present new view controllers as cards

CardStackController iOS custom controller used in the Jobandtalent app to present new view controllers as cards. This controller behaves very similar

jobandtalent 534 Dec 1, 2021