FlightAnimator is a natural animation engine built on top of CoreAnimation. Implemented with a blocks based approach it is very easy to create, configure, cache, and reuse animations dynamically based on the current state.
FlightAnimator uses CAKeyframeAnimation(s) and CoreAnimationGroup(s) under the hood. You can apply animations on a view directly, or cache animations to define states to apply at a later time. The animations are technically a custom CAAnimationGroup, once applied to the layer, will dynamically synchronize the remaining progress based on the current presentationLayer's values.
There are two ways you can use this framework, you can perform an animation on a specific view, or register an animation on a view to perform later.
When creating or registering an animation, the frame work uses a blocks cased approach to build the animation. You can apply a value, a timing, and set the primary flag, which will be discussed at a later point in the documentation.
To perform a simple animation call the animate(:)
method on the view you want to animate. Let's look at a simple example below.
view.animate { (animator) in animator.bounds(newBounds).duration(0.5).easing(.EaseOutCubic) animator.position(newPositon).duration(0.5).easing(.EaseInSine) }
The closure returns an instance of an FAAnimationMaker, which can be used to build a complex animation to perform, one property at a time. You can apply different durations, and easing curves for each individual property in the animation. And that's it, the animation kicks itself off, applies the final animation to the layer, and sets all the final layers values on the model layer.
In the case you have defined a custom NSManaged animatable property, i.e progress to draw a circle. You can use the value(value:forKeyPath:)
method on the animator to animate that property.
view.animate { (animator) in animator.value(value, forKeyPath : "progress").duration(0.5).easing(.EaseOutCubic) }
Chaining animations together in flight animator is very easy, and allows you to trigger another animation based on the time progress, or the value progress of an animation.
You can nest a trigger on a parent animation at a specified progress, and trigger which will trigger accordingly, and can be applied to the view being animated, or any other view define.
Let's look at how we can nest some animations using time and value based progress triggers.
A time based trigger will apply the next animation based on the the progressed time of the overall parent animation. Below is an examples that will trigger the second animation at the halfway point in time of the parent animation by calling triggerAtTimeProgress(...)
view.animate { (animator) in animator.bounds(newBounds).duration(0.5).easing(.EaseOutCubic) animator.position(newPositon).duration(0.5).easing(.EaseOutCubic) animator.triggerAtTimeProgress(atProgress: 0.5, onView: self.secondaryView, animator: { (animator) in animator.bounds(newSecondaryBounds).duration(0.5).easing(.EaseOutCubic) animator.position(newSecondaryCenter).duration(0.5).easing(.EaseOutCubic) }) }
A value based progress trigger will apply the next animation based on the the value progress of the overall parent animation. Below is an examples that will trigger the second animation at the halfway point of the value progress on the parent animation by calling animator.triggerAtValueProgress(...)
view.animate { (animator) in animator.bounds(newBounds).duration(0.5).easing(.EaseOutCubic) animator.position(newPositon).duration(0.5).easing(.EaseOutCubic) animator.triggerAtValueProgress(atProgress: 0.5, onView: self.secondaryView, animator: { (animator) in animator.bounds(newSecondaryBounds).duration(0.5).easing(.EaseOutCubic) animator.position(newSecondaryCenter).duration(0.5).easing(.EaseOutCubic) }) }
You can define animation states up fron using keys, and triggers then at any other time in your application flow. When the animation is applied, if the view is in mid flight, it will synchronize itself accordingly, and animate to it's final destination. To register an animation, you can call a glabally defined method, and just as you did earlier define the property animations within the maker block.
The following example shows how to register, and cache it for a key on a specified view view. This animation is only cached, and is not performed until it is manually triggered at a later point.
struct AnimationKeys { static let CenterStateFrameAnimation = "CenterStateFrameAnimation" } ... registerAnimation(onView : view, forKey : AnimationKeys.CenterStateFrameAnimation) { (animator) in animator.bounds(newBounds).duration(0.5).easing(.EaseOutCubic) animator.position(newPositon).duration(0.5).easing(.EaseOutCubic) })
To trigger the animation all you have to do is call the following
view.applyAnimation(forKey: AnimationKeys.CenterStateFrameAnimation)
In the case there is a need to apply the final values without actually animating the view, you can 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)
To Be Continued
A good reference for the supported easings can be found here
EaseInSine EaseOutSine EaseInOutSine EaseOutInSine | EaseInQuadratic EaseOutQuadratic EaseInOutQuadratic EaseOutInQuadratic | EaseInCubic EaseOutCubic EaseInOutCubic EaseOutInCubic |
EaseInQuartic EaseOutQuartic EaseInOutQuartic EaseOutInQuartic | EaseInQuintic EaseOutQuintic EaseInOutQuintic EaseOutInQuintic | EaseInExponential EaseOutExponential EaseInOutExponential EaseOutInExponential |
EaseInCircular EaseOutCircular EaseInOutCircular EaseOutInCircular | EaseInBack EaseOutBack EaseInOutBack EaseOutInBack | EaseInElastic EaseOutElastic EaseInOutElastic EaseOutInElastic |
EaseInBounce EaseOutBounce EaseInOutBounce EaseOutInBounce | Linear LinearSmooth LinearSmoother |
FlightAnimator also supports any user defined animatable properties of the following types:
The following is a support chart for animatable CALayer properties
anchorPoint | Supported with all easing Curves |
backgroundColor | To be supported in a later release |
backgroundFilters | Possible support without easing curves in a later version without |
borderColor | To be supported in a later release |
borderWidth | Supported with all easing Curves |
bounds | Supported with all easing Curves |
compositingFilter | Possible support without easing curves in a later version without |
contents | Possible support without easing curves in a later version without |
contentsRect | Supported with all easing Curves |
cornerRadius | To be supported in a later release |
doubleSided | Possible support without easing curves in a later version |
filters | Supported with all easing Curves |
frame | Supported by combining bounds and position |
hidden | Possible support without easing curves in a later version |
mask | Possible support without easing curves in a later version |
masksToBounds | Possible support without easing curves in a later version |
opacity | Supported with all easing Curves |
position | Supported with all easing Curves |
shadowColor | To be supported in a later release |
shadowOffset | Supported with all easing Curves |
shadowOpacity | Supported with all easing Curves |
shadowPath | Possible support without easing curves in a later version |
shadowRadius | Supported with all easing Curves |
sublayers | Possible support without easing curves in a later version |
sublayerTransform | Supported with all easing Curves |
transform | Supported with all easing Curves |
zPosition | Supported with all easing Curves |
FlightAnimator is released under the MIT license. SeeLicense for details.