Advanced Natural Motion Animations, Simple Blocks Based Syntax

Overview

FlightAnimator

Cocoapods Compatible Carthage compatible Build Status Platform License Join the chat at https://gitter.im/AntonTheDev/FlightAnimator

alt tag

Moved to Swift 3.1 Support:

Introduction

FlightAnimator provides a very simple blocks based animation definition language that allows you to dynamically create, configure, group, sequence, cache, and reuse property animations.

Unlike CAAnimationGroups, and UIViewAnimations, which animate multiple properties using a single easing curve, FlightAnimator allows configuration, and synchronization, of unique easing curves per individual property animation.

Features

  • 46+ Parametric Curves, Decay, and Springs
  • Blocks Syntax for Building Complex Animations
  • Chain and Sequence Animations:
  • Apply Unique Easing per Property Animation
  • Advanced Multi-Curve Group Synchronization
  • Define, Cache, and Reuse Animations

Check out the FlightAnimator Project Demo in the video below to
experiment with all the different capabilities of the FlightAnimator.

FlightAnimator Demo

Installation

Communication

  • If you found a bug, or have a feature request, open an issue.
  • If you need help or a general question, use Stack Overflow. (tag 'flight-animator')
  • If you want to contribute, review the Contribution Guidelines, and submit a pull request.

Basic Use

FlightAnimator provides a very flexible syntax for defining animations ranging in complexity with ease. Following a blocks based builder approach you can easily define an animation group, and it's property animations in no time.

Under the hood animations built are CAAnimationGroup(s) with multiple custom CAKeyframeAnimation(s) defined uniquely per property. Once it's time to animate, FlightAnimator will dynamically synchronize the remaining progress for all the animations relative to the current presentationLayer's values, then continue to animate to it's final state.

Simple Animation

To really see the power of FlightAnimator, let's first start by defining an animation using CoreAnimation, then re-define it using the framework's blocks based syntax. The animation below uses a CAAnimationGroup to group 3 individual CABasicAnimations for alpha, bounds, and position.

let alphaAnimation 			= CABasicAnimation(keyPath: "position")
alphaAnimation.toValue 			= 0.0
alphaAnimation.fromValue 		= 1.0
alphaAnimation.fillMode              	= kCAFillModeForwards
alphaAnimation.timingFunction        	= CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseOut)

let boundsAnimation 			= CABasicAnimation(keyPath: "bounds")
boundsAnimation.toValue 		= NSValue(CGRect : toBounds)
boundsAnimation.fromValue 		= NSValue(CGRect : view.layer.bounds)
boundsAnimation.fillMode              	= kCAFillModeForwards
boundsAnimation.timingFunction        	= CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseOut)

let positionAnimation 			= CABasicAnimation(keyPath: "position")
positionAnimation.toValue 		= NSValue(CGPoint : toPosition)
positionAnimation.fromValue 		= NSValue(CGPoint : view.layer.position)
positionAnimation.fillMode              = kCAFillModeForwards
positionAnimation.timingFunction        = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseOut)

let progressAnimation 			= CABasicAnimation(keyPath: "animatableProgress")
progressAnimation.toValue 		= 1.0
progressAnimation.fromValue 		= 0
progressAnimation.fillMode              = kCAFillModeForwards
progressAnimation.timingFunction        = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseOut)

let animationGroup 			= CAAnimationGroup()
animationGroup.duration 		= 0.5
animationGroup.removedOnCompletion   	= true
animationGroup.animations 		= [alphaAnimation,  boundsAnimation, positionAnimation, progressAnimation]

view.layer.addAnimation(animationGroup, forKey: "PositionAnimationKey")
view.frame = toFrame

Now that we saw the example above. Let's re-define FlightAnimator's blocks based syntax

view.animate {  [unowned self] (animator) in
	animator.alpha(toAlpha).duration(0.5).easing(.OutCubic)
	animator.bounds(toBounds).duration(0.5).easing(.OutCubic)
      	animator.position(toPosition).duration(0.5).easing(.OutCubic)
      	animator.value(toProgress, forKeyPath : "animatableProgress").duration(0.5).easing(.OutCubic)
}

Calling animate(:) on the view begins the FAAnimationGroup creation process. Inside the closure the animator creates, configures, then appends custom animations to the newly created parent group. Define each individual property animation by calling one of the pre-defined property setters, and/or the func value(:, forKeyPath:) -> PropertyAnimator method for any other animatable property.

Once the property animation is initiated, recursively configure the PropertyAnimator by chaining duration, easing, and/or primary designation, to create the final FABasicAnimation, and add it to the parent group.

func duration(duration : CGFloat) -> PropertyAnimator
func easing(easing : FAEasing) -> PropertyAnimator
func primary(primary : Bool) -> PropertyAnimator

Once the function call exits the closure, FlightAnimator performs the following:

  1. Adds the newly created FAAnimationGroup to the calling view's layer,
  2. Synchronizes the grouped FABasicAnimations relative to the calling view's presentation layer values
  3. Triggers the animation by applying the toValue from the grouped animations to to the calling view's layer.

Chaining Animations

Chaining animations together in FlightAnimator is simple.

Trigger on Start

The animation created on the secondaryView is triggered once the the primaryView's animation begins.

primaryView.animate { [unowned self] (animator) in
	....

    animator.triggerOnStart(onView: self.secondaryView, animator: { (animator) in
         ....
    })
}

Trigger on Completion

The animation created on the secondaryView is triggered once the the primaryView's animation completes.

primaryView.animate { [unowned self] (animator) in
	....

    animator.triggerOnCompletion(onView: self.secondaryView, animator: { (animator) in
         ....
    })
}

Time Progress Trigger

The animation created on the secondaryView is triggered when the driving animation reaches the relative half way point in duration on the primaryView's animation.

primaryView.animate { [unowned self] (animator) in
	....

    animator.triggerOnProgress(0.5, onView: self.secondaryView, animator: { (animator) in
         ....
    })
}

Value Progress Trigger

The animation created on the secondaryView is triggered when the driving animation reaches the relative half way point between the fromValue and toValue of the primaryView's animation. This is driven

primaryView.animate { [unowned self] (animator) in
	....

    animator.triggerOnValueProgress(0.5, onView: self.secondaryView, animator: { (animator) in
         ....
    })
}

Nesting Animation Triggers

There is built in support for nesting triggers within triggers to sequence animations, and attach multiple types of triggers relative to the scope of the parent animation.

primaryView.animate { [unowned self] (animator) in
	....

    animator.triggerOnStart(onView: self.secondaryView, animator: { (animator) in
         -> Relative to primaryView animation

	animator.triggerOnCompletion(onView: self.tertiaryView, animator: { (animator) in
		-> Relative to secondaryView animation

        	animator.triggerOnProgress(0.5, onView: self.quaternaryView, animator: { (animator) in
			-> Relative to tertiaryView animation
    		})
     	})

        animator.triggerOnValueProgress(0.5, onView: self.quinaryView, animator: { (animator) in
         		-> Relative to secondaryView animation
    	})
    })

    animator.triggerOnStart(onView: self.senaryView, animator: { (animator) in
         	-> Relative to primaryView animation
    })
}

CAAnimationDelegate Callbacks

Sometimes there is a need to perform some logic on the start of an animation, or the end of the animation by responding to the CAAnimationDelegate methods

view.animate { (animator) in
    ....

    animator.setDidStartCallback({ (animator) in
         // Animation Did Start
    })

    animator.setDidStopCallback({ (animator, complete) in
         // Animation Did Stop   
    })
}

These can be nested just as the animation triggers, and be applied animator on the group in scope of the animation creation closure by the animator.

Cache & Reuse Animations

FlightAnimator allows for registering animations (aka states) up front with a unique animation key. Once defined it can be manually triggered at any time in the application flow using the animation key used registration.

When the animation is applied, if the view is in mid flight, it will synchronize itself with the current presentation layer values, and animate to its final destination.

Register/Cache Animation

To register an animation, call a globally defined method, and create an animations just as defined earlier examples within the maker block. The following example shows how to register, and cache an animation for a key on a specified view.

struct AnimationKeys {
	static let CenterStateFrameAnimation  = "CenterStateFrameAnimation"
}
...

view.registerAnimation(forKey : AnimationKeys.CenterStateFrameAnimation) { (animator) in
      animator.bounds(newBounds).duration(0.5).easing(.OutCubic)
      animator.position(newPositon).duration(0.5).easing(.OutCubic)
})

This animation is only cached, and is not performed until it is manually triggered.

Apply Registered Animation

To trigger the animation call the following

view.applyAnimation(forKey: AnimationKeys.CenterStateFrameAnimation)

To apply final values without animating the view, override the default animated flag to false, and it will apply all the final values to the model layer of the associated view.

view.applyAnimation(forKey: AnimationKeys.CenterStateFrameAnimation, animated : false)

Advanced Use

Timing Adjustments

Due to the dynamic nature of the framework, it may take a few tweaks to get the animation just right.

FlightAnimator has a few options for finer control over timing synchronization:

  • Timing Priority - Adjust how the time is select during synchronization of the overall animation
  • Primary Drivers - Defines animations that affect timing during synchronization of the overall animation

Timing Priority

First a little background, the framework basically does some magic so synchronize the time by prioritizing the maximum time remaining based on progress if redirected in mid flight.

Lets look at the following example of setting the timingPriority on a group animation to .MaxTime, which is the default value for FlightAnimator.

func animateView(toFrame : CGRect) {

	let newBounds = CGRectMake(0,0, toFrame.width, toFrame.height)
	let newPosition = CGPointMake(toFrame.midX, toFrame.midY)

	view.animate(.MaxTime) { (animator) in
      		animator.bounds(newBounds).duration(0.5).easing(.OutCubic)
      		animator.position(newPositon).duration(0.5).easing(.InSine)
	}
}

Just like the demo app, This method gets called by different buttons, and takes on the frame value of button that triggered the method. Let's the animation has been triggered, and is in mid flight. While in mid flight another button is tapped, a new animation is applied, and ehe position changes, but the bounds stay the same.

Internally the framework will figure out the current progress in reference to the last animation, and will select the max duration value from the array of durations on the grouped property animations.

Lets assume the bounds don't change, thus animation's duration is assumed to be 0.0 after synchronization. The new animation will synchronize to the duration of the position animation based on progress, and automatically becomes the max duration based on the .MaxTime timing priority.

The timing priority can also be applied on triggerAtTimeProgress() or triggerAtValueProgress(). Now this leads into the next topic, and that is the primary flag.

The more property animations within a group, the more likely the need to adjust how the timing is applied. For this purpose there are 4 timing priorities to choose from:

  • .MaxTime
  • .MinTime
  • .Median
  • .Average

Primary Flag

As in the example prior, there is a mention that animations can get quite complex, and the more property animations within a group, the more likely the animation will have a hick-up in the timing, especially when synchronizing 4+ animations with different curves and durations.

For this purpose, set the primary flag on individual property animations, and designate them as primary duration drivers. By default, if no property animation is set to primary, during synchronization, FlightAnimator will use the timing priority setting to find the corresponding value from all the animations after progress synchronization.

If we need only some specific property animations to define the progress accordingly, and become the primary drivers, set the primary flag to true, which will exclude any other animation which is not marked as primary from consideration.

Let's look at an example below of a simple view that is being animated from its current position to a new frame using bounds and position.

view.animate(.MaxTime) { (animator) in
      animator.bounds(newBounds).duration(0.5).easing(.OutCubic).primary(true)
      animator.position(newPositon).duration(0.5).easing(.InSine).primary(true)
      animator.alpha(0.0).duration(0.5).easing(.OutCubic)
      animator.transform(newTransform).duration(0.5).easing(.InSine)
}

Simple as that, now when the view is redirected during an animation in mid flight, only the bounds and position animations will be considered as part of the timing synchronization.

.SpringDecay w/ Initial Velocity

When using a UIPanGestureRecognizer to move a view around on the screen by adjusting its position, and say there is a need to smoothly animate the view to the final destination right as the user lets go of the gesture. This is where the .SpringDecay easing comes into play. The .SpringDecay easing will slow the view down easily into place, all that need to be configured is the initial velocity, and it will calculate its own time relative to the velocity en route to its destination.

Below is an example of how to handle the handoff and use .SpringDecay(velocity: velocity) easing to perform the animation.

func respondToPanRecognizer(recognizer : UIPanGestureRecognizer) {
    switch recognizer.state {
    ........

    case .Ended:
    	let currentVelocity = recognizer.velocityInView(view)

      	view.animate { (animator) in
         	animator.bounds(finalBounds).duration(0.5).easing(.OutCubic)
  			animator.position(finalPositon).duration(0.5).easing(.SpringDecay(velocity: velocity))
      	}
    default:
        break
    }
}

Reference

Supported Parametric Curves

CALayer's Supported Animatable Property

Current Release Notes

Contribution Guidelines

Framework Demo App

The project includes a highly configurable demo app that allows for experimentation to explore resulting effects of the unlimited configurations FlightAnimator supports.

Demo Features Included:

  • Animate a view to different location on the screen
  • Drag and release view to apply Decay easing to the final destination
  • Adjust timing curves for bounds, position, alpha, and transform.
  • Enable a secondary view, which follows the main view to it's last location
  • Adjust group timing priority to test synchronization
  • Adjust progress for time based/value based triggers on the secondary view

License

FlightAnimator is released under the MIT license. See License for details.

Comments
  • CATransform3D Bug - M33 Property Pointing to M34

    CATransform3D Bug - M33 Property Pointing to M34

    in CATransform3D springs[SpringAnimationKey.M33] = self.m33.interpolationSprings((toValue as! CATransform3D).m34, initialVelocity : startingVelocity.x, angularFrequency : angularFrequency, dampingRatio : dampingRatio)[SpringAnimationKey.CGFloat] m34 must change to m33

    opened by springlo 3
  • Demo app doesn't build out of the box

    Demo app doesn't build out of the box

    Steps to reproduce:

    1. Install CocoaPods 1.2.1
    2. Run pod try FlightAnimator
    3. Xcode opens with the project.
    4. Click "Build and Run"

    Expected results: project runs

    Actual results:

    /var/folders/j_/86_9xkgj0pqc0q2h5xx_9lt40000gn/T/CocoaPods/Try/FlightAnimator/FlightAnimator-Demo/FlightAnimator-Demo/ViewController.swift:2:8: error: no such module 'FlightAnimator'
    import FlightAnimator
           ^
    
    opened by ZevEisenberg 1
  • Feature - Elegant Integration of CAAnimationDelegate Callbacks

    Feature - Elegant Integration of CAAnimationDelegate Callbacks

    Currently there is support for animationDidStart and animationDidStop CAAnimationDelegate callbacks, but this functionality needs to be integrated into the framework elegantly:

    • Need to find a solution on how to syntactically integrate the callbacks inline
    • Need to ensure that the callbacks are cancelled if the animation is stopped
    enhancement 
    opened by AntonTheDev 1
  • Feature - Implement Reverse Animation Feature

    Feature - Implement Reverse Animation Feature

    Update framework to support reverse animations:

    • Implement a way to set an animation as reversable
    • Update scrubbing animations functionality to support this at the half way point
    • Ensure that all sequenced animations are accounted for during scrubbing
    enhancement 
    opened by AntonTheDev 1
  • Feature - Implement Interpolation for UIColor & CGColor

    Feature - Implement Interpolation for UIColor & CGColor

    Update framework to support UIColor / CGColor interpolation:

    • Implementing the FAAnimatable protocol for UIColor / CGColor
    • This should support the CALayer Animatable color properties
    • Update FAAnimationGroup to ensure it can handle UIColor / CGColor
    • Update Demo app to test and reflect the color property
    enhancement 
    opened by AntonTheDev 1
  • Add a Gitter chat badge to README.md

    Add a Gitter chat badge to README.md

    AntonTheDev/FlightAnimator now has a Chat Room on Gitter

    @AntonTheDev has just created a chat room. You can visit it here: https://gitter.im/AntonTheDev/FlightAnimator.

    This pull-request adds this badge to your README.md:

    Gitter

    If my aim is a little off, please let me know.

    Happy chatting.

    PS: Click here if you would prefer not to receive automatic pull-requests from Gitter in future.

    opened by gitter-badger 0
  • Maintenance - Remove Unnecessary Type Conversion

    Maintenance - Remove Unnecessary Type Conversion

    Run through the code and ensure that there are no type conversions between Double, Float and CGFloat. This should have a minor performance improvement while making the code more legible.

    maintenance 
    opened by AntonTheDev 0
  • Research - Support for watchOS / tvOS

    Research - Support for watchOS / tvOS

    Need to research on what it would take to support watchOS and tvOS for the framework. Unsure of the level of support for CoreAnimation. Need to look into if this is possible.

    research 
    opened by AntonTheDev 0
  • Maintenance - Refactor Value Progress Methods

    Maintenance - Refactor Value Progress Methods

    Figure out a way to generalize the value progress calculation for the sequence trigger:

    • Should not have to cast it to a supported type within FAAnimation
    • Look into generics and how one would go about implement them by abstracting them completely from the core logic
    maintenance 
    opened by AntonTheDev 0
  • Maintenance - Update Cocoadoc Documentation

    Maintenance - Update Cocoadoc Documentation

    Add standard CocoaDocs for the framework

    • Document code within framework accordingly
    • Most importantly document the publicly exposed methods
    • Move the FlightAnimator class to the top of the group hierarchy within the project browser
    maintenance 
    opened by AntonTheDev 0
  • Respect slow animations in the iOS simulator

    Respect slow animations in the iOS simulator

    It probably won't be easy, but this may help: http://stackoverflow.com/questions/13275294/detect-if-slow-animations-is-on-off-in-ios-simulator-in-code

    opened by ZevEisenberg 5
  • Add ability to have delay in PropertyAnimationConfig

    Add ability to have delay in PropertyAnimationConfig

    Hi, I'm using your library, and it's a very nice work, but I was wondering if you were thinking of adding a delay property to the PropertyAnimationConfig. I know there's a triggerAtTimeProgress hook for a prior animation, and that's quite useful, but sometimes a simple delay is a better course of action.

    opened by jcnes 1
  • Repeating animations

    Repeating animations

    I'm most likely just missing something, but I don't see any property that lets me specify that I want an animation and its sub animations to repeat. Is there something to help with this use case?

    opened by jeremytregunna 3
  • Feature - Reversing / Scrubbing Sequenced Animations

    Feature - Reversing / Scrubbing Sequenced Animations

    Update framework to support scrubbing across sequenced animations:

    • Currently the framework only supports scrubbing across a single FAAnimationGroup
    • Need to implement ability to trigger/pause and scrub sub animations that are set up as part of an animation chain.
    • Break off FAPanGestureRecognizer into a separate library

    Update framework to support reverse animations:

    • Implement a way to set an animation as reversible
    • Update scrubbing animations functionality to support this at the half way point
    • Ensure that all sequenced animations are accounted for during scrubbing
    enhancement 
    opened by AntonTheDev 0
Owner
Anton
Lead Mobile Architect / Engineer
Anton
A Swift library to take the power of UIView.animateWithDuration(_:, animations:...) to a whole new level - layers, springs, chain-able animations and mixing view and layer animations together!

ver 2.0 NB! Breaking changes in 2.0 - due to a lot of requests EasyAnimation does NOT automatically install itself when imported. You need to enable i

Marin Todorov 3k Dec 27, 2022
A port of SwiftUILab's Advanced Animations that also supports macOS

SwiftUILab Advanced Animations on the Mac as well A port of SwiftUILab's Advanced Animations that also supports macOS Here's the Ghist of the original

Mihaela Mihaljevic Jakic 10 Jan 2, 2023
Easily build advanced custom animations on iOS.

INTUAnimationEngine makes it easy to build advanced custom animations on iOS. INTUAnimationEngine provides a friendly interface to drive custom animat

Intuit 1.1k Dec 14, 2022
☠️SkeletonUI aims to bring an elegant, declarative syntax to skeleton loading animations.

SkeletonUI aims to bring an elegant, declarative syntax to skeleton loading animations. Get rid of loading screens or spinners and start using skeletons to represent final content shapes.

Carlos Solana Martínez 551 Dec 18, 2022
A collection of animations for iOS. Simple, just add water animations.

DCAnimationKit A collection of animations for iOS Simply, just add water! DCAnimationKit is a category on UIView to make animations easy to perform. E

Dalton 797 Sep 23, 2022
(Animate CSS) animations for iOS. An easy to use library of iOS animations. As easy to use as an easy thing.

wobbly See Wobbly in action (examples) Add a drop of honey ?? to your project wobbly has a bunch of cool, fun, and easy to use iOS animations for you

Sagaya Abdulhafeez 150 Dec 23, 2021
(Animate CSS) animations for iOS. An easy to use library of iOS animations. As easy to use as an easy thing.

wobbly See Wobbly in action (examples) Add a drop of honey ?? to your project wobbly has a bunch of cool, fun, and easy to use iOS animations for you

Sagaya Abdulhafeez 150 Dec 23, 2021
Physics-based animations for iOS, tvOS, and macOS.

Advance An animation library for iOS, tvOS, and macOS that uses physics-based animations (including springs) to power interactions that move and respo

Tim Donnelly 4.5k 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
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
Fluid - Use a declarative syntax to build your user interface using UIKit like SwiftUI

Fluid Fluid is powered by ResultBuilder and a custom layout engine. You can uses

HZ.Liu 13 Dec 9, 2022
DaisyChain is a micro framework which makes UIView animations chaining dead simple.

DaisyChain DaisyChain is a micro framework which makes UIView animations chaining dead simple. It uses the exact same interface you are familiars with

Ali Karagoz 31 Nov 3, 2022
Designed for gesture-driven animations. Fast, simple, & extensible!

Yet Another Animation Library Designed for gesture-driven animations. Fast, simple, & extensible! It is written in pure swift 3.1 with protocol orient

Luke Zhao 509 Dec 21, 2022
Simple UIButton subclass with additional state change animations (e.g. backgroundColor) and missing features

SimpleButton UIButton subclass with animated, state-aware attributes. Easy to subclass and configure! Full API docs Usage Just create your own SimpleB

Andreas Tinoco Lobo 169 Sep 14, 2022
A concept to more easily define simple keyframe / multi-step animations in SwiftUI

?? Animate A concept to more easily define simple keyframe / multi-step animations in SwiftUI, without: Defining an @State value for each property to

Seb Jachec 3 Oct 18, 2022
Ease is an event driven animation system that combines the observer pattern with custom spring animations as observers

Ease is an event driven animation system that combines the observer pattern with custom spring animations as observers. It's magic. Features Animate a

Robert-Hein Hooijmans 1.3k Nov 17, 2022
Swift interpolation for gesture-driven animations

Interpolate Interpolate is a powerful Swift interpolation framework for creating interactive gesture-driven animations. Usage The ?? idea of Interpola

Roy Marmelstein 1.8k Dec 20, 2022
An iOS library to natively render After Effects vector animations

Lottie for iOS, macOS (and Android and React Native) View documentation, FAQ, help, examples, and more at airbnb.io/lottie Lottie is a mobile library

Airbnb 23.6k Dec 31, 2022