An iOS 8+-compatible animator that combines the best aspects of modern UIView and CALayer animation APIs.
Check out a local copy of the repo to access the Catalog application by running the following commands:
git clone https://github.com/material-motion/motion-animator-objc.git
cd motion-animator-objc
pod install
open MotionAnimator.xcworkspace
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
- How to animate explicit layer properties
- How to animate like UIView
- How to animate a transition
- How to animate an interruptible transition
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;
view.center = before;
[UIView animateWithDuration:0.5 animations:^{
view.center = 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;
view.center = before;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.7 initialSpringVelocity:0 options:0 animations:^{
view.center = 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;
view.center = offscreen;
[_animator animateWithTiming:kMotionSpec animations:^{
view.center = 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
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
toLayer:chipView.layer
withValues:@[ @(chipFrame.size.height), @(headerFrame.size.height) ]
keyPath:MDMKeyPathHeight];
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.
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
toLayer:view.layer
withValues:@[ @(collapsedHeight), @(expandedHeight) ]
keyPath:MDMKeyPathHeight];
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.
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.