MotionAnimator 2.8.1

MotionAnimator 2.8.1

LangLanguage Obj-CObjective C
License Apache 2
ReleasedLast Release Mar 2018

Maintained by Jeff Verkoeyen, Adrian Secord, Ian Gordon.




Pod Tries2,402
Test Targets191
powered by Segment


Pull Requests0


LOCLines of Code 728

  • By
  • The Material Motion Authors

Motion Animator

A Motion Animator creates performant, interruptible animations from motion specs.

Build Status
CocoaPods Compatible

This library provides APIs that turn Motion Interchange
motion specifications into animations.

What is a motion specification?

A motion specification defines the delay, duration, and acceleration of animations in a simple
data format that can live separate from your animation logic.

For example, let's say we wanted to describe the motion for this animation:

We might create a specification like so:

struct CalendarChipTiming {
  MDMMotionTiming chipWidth;
  MDMMotionTiming chipHeight;
  MDMMotionTiming chipY;

  MDMMotionTiming chipContentOpacity;
  MDMMotionTiming headerContentOpacity;

  MDMMotionTiming navigationBarY;
typedef struct CalendarChipTiming CalendarChipTiming;

struct CalendarChipMotionSpec {
  CalendarChipTiming expansion;
  CalendarChipTiming collapse;
typedef struct CalendarChipMotionSpec CalendarChipMotionSpec;

FOUNDATION_EXTERN struct CalendarChipMotionSpec CalendarChipSpec;

With our implementation of the spec looking like so:

struct CalendarChipMotionSpec CalendarChipSpec = {
  .expansion = {
    .chipWidth = {
      .delay = 0.000, .duration = 0.285, .curve = MDMEightyForty,
    .chipHeight = {
      .delay = 0.015, .duration = 0.360, .curve = MDMEightyForty,
  .collapse = {
    .chipWidth = {
      .delay = 0.045, .duration = 0.330, .curve = MDMEightyForty,
    .chipHeight = {
      .delay = 0.000, .duration = 0.330, .curve = MDMEightyForty,

Our spec defines two different transition states: expansion and collapse. At runtime, we determine
which of these two specs we need and then use the timings to animate our views with an instance of

CalendarChipTiming timing = _expanded ? CalendarChipSpec.expansion : CalendarChipSpec.collapse;

MDMMotionAnimator *animator = [[MDMMotionAnimator alloc] init];
animator.shouldReverseValues = !_expanded;

[animator animateWithTiming:timing.chipHeight
                 withValues:@[ @(chipFrame.size.height), @(headerFrame.size.height) ]

A working implementation of this example can be seen in the included example app.

Example apps/unit tests

Check out a local copy of the repo to access the Catalog application by running the following

git clone
cd motion-animator-objc
pod install
open MotionAnimator.xcworkspace


Installation with CocoaPods

CocoaPods is a dependency manager for Objective-C and Swift libraries. CocoaPods automates the
process of using third-party libraries in your projects. See
the Getting Started guide for more
information. You can install it with the following command:

gem install cocoapods

Add motion-animator to your Podfile:

pod 'MotionAnimator'

Then run the following command:

pod install


Import the framework:

@import MotionAnimator;

You will now have access to all of the APIs.


How to make a spec from existing animations

A motion spec is a complete representation of the motion curves that meant to be applied during an
animation. Your motion spec might consist of a single MDMMotionTiming instance, or it might be a
nested structure of MDMMotionTiming instances, each representing motion for a different part of a
larger animation. In either case, your magic motion constants now have a place to live.

Consider a simple example of animating a view on and off-screen. Without a spec, our code might look
like so:

CGPoint before = dismissing ? onscreen : offscreen;
CGPoint after = dismissing ? offscreen : onscreen; = before;
[UIView animateWithDuration:0.5 animations:^{ = after;

What if we want to change this animation to use a spring curve instead of a cubic bezier? To do so
we'll need to change our code to use a new API:

CGPoint before = dismissing ? onscreen : offscreen;
CGPoint after = dismissing ? offscreen : onscreen; = before;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0 options:0 animations:^{ = after;
} completion:nil];

Now let's say we wrote the same code with a motion spec and animator:

MDMMotionTiming motionSpec = {
  .duration = 0.5, .curve = MDMMotionCurveMakeSpring(1, 100, 1),

MDMMotionAnimator *animator = [[MDMMotionAnimator alloc] init];
animator.shouldReverseValues = dismissing; = offscreen;
[_animator animateWithTiming:kMotionSpec animations:^{ = onscreen;

Now if we want to change our motion back to an easing curve, we only have to change the spec:

MDMMotionTiming motionSpec = {
  .duration = 0.5, .curve = MDMMotionCurveMakeBezier(0.4f, 0.0f, 0.2f, 1.0f),

The animator code stays the same. It's now possible to modify the motion parameters at runtime
without affecting any of the animation logic.

This pattern is useful for building transitions and animations. To learn more through examples,
see the following implementations:

Material Components Activity Indicator

Material Components Progress View

Material Components Masked Transition

How to animate explicit layer properties

MDMMotionAnimator provides an explicit API for adding animations to animatable CALayer key paths.
This API is similar to creating a CABasicAnimation and adding it to the layer.

[animator animateWithTiming:timing.chipHeight
                 withValues:@[ @(chipFrame.size.height), @(headerFrame.size.height) ]

How to animate like UIView

MDMMotionAnimator provides an API that is similar to UIView's animateWithDuration:. Use this API
when you want to apply the same timing to a block of animations:

chipView.frame = chipFrame;
[animator animateWithTiming:timing.chipHeight animations:^{
  chipView.frame = headerFrame;
// chipView.layer's position and bounds will now be animated with timing.chipHeight's timing.

How to animate a transition

Start by creating an MDMMotionAnimator instance.

MDMMotionAnimator *animator = [[MDMMotionAnimator alloc] init];

When we describe our transition we'll describe it as though we're moving forward and take advantage
of the shouldReverseValues property on our animator to handle the reverse direction.

animator.shouldReverseValues = isTransitionReversed;

To animate a property on a view, we invoke the animate method. We must provide a timing, values,
and a key path:

[animator animateWithTiming:timing
                 withValues:@[ @(collapsedHeight), @(expandedHeight) ]

How to animate an interruptible transition

MDMMotionAnimator is configured by default to generate interruptible animations using Core
Animation's additive animation APIs. You can simply re-execute the animate calls when your
transition's direction changes and the animator will add new animations for the updated direction.

Helpful literature


We welcome contributions!

Check out our upcoming milestones.

Learn more about our team,
our community, and
our contributor essentials.


Licensed under the Apache 2.0 license. See LICENSE for details.