Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

Commit

Permalink
Merge branch 'release-candidate' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Verkoeyen committed Nov 10, 2017
2 parents 92996ad + f1c48bd commit 178fbc3
Show file tree
Hide file tree
Showing 15 changed files with 487 additions and 68 deletions.
7 changes: 7 additions & 0 deletions .jazzy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module: MotionAnimator
module_version: 2.2.0
sdk: iphonesimulator
umbrella_header: src/MotionAnimator.h
objc: true
github_url: https://github.com/material-motion/motion-animator-objc
github_file_prefix: https://github.com/material-motion/motion-animator-objc/tree/v2.2.0
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
# 2.2.0

This minor release introduces support for the new initial velocity spring curve value in
MotionInterchange v1.3.0. This release also includes additional public and internal documentation.

## Dependency changes

The minimum MotionInterchange version has been increased to v1.3.0.

## New features

`MDMMotionAnimator` now supports initial velocity for spring curves.

## Source changes

* [Use MotionCurve make methods to create motion timings in the tests. (#38)](https://github.com/material-motion/motion-animator-objc/commit/f80203d711126130a5d58c880bae0cea4c72b6e7) (featherless)
* [Extract initial velocity from the motion timing. (#37)](https://github.com/material-motion/motion-animator-objc/commit/ab431bd2416b43ce601b16fbb4abdb3b9eba851e) (featherless)
* [Silence a deprecation warning in motion interchange 1.3.0.](https://github.com/material-motion/motion-animator-objc/commit/29b551ae730f1a48f37793c65bf14d761a544b6b) (Jeff Verkoeyen)

## Non-source changes

* [Bump MotionInterchange dependency to 1.3.](https://github.com/material-motion/motion-animator-objc/commit/3b543b856df8543d8af4127b8d5536fb31100d3b) (Jeff Verkoeyen)
* [Ensure that deprecations are treated as warnings, not errors, when building with CocoaPods.](https://github.com/material-motion/motion-animator-objc/commit/b1289ea58130aba8e8dc2455989130db9f8be5ed) (Jeff Verkoeyen)
* [Move example project up.](https://github.com/material-motion/motion-animator-objc/commit/800b2996ba39746adbdfe4bf19c18ccc37f2bc91) (Jeff Verkoeyen)
* [Formatting.](https://github.com/material-motion/motion-animator-objc/commit/0a7dac13f196c5d9774fe5f712a0f8b1b0a4026e) (Jeff Verkoeyen)
* [Add more tutorials and rework the introduction.](https://github.com/material-motion/motion-animator-objc/commit/f25998e6d7161ed46f89bdab799c1be678dc98a9) (Jeff Verkoeyen)
* [Add a guide on building motion specs.](https://github.com/material-motion/motion-animator-objc/commit/f34172534d153ff461fc57943abc81605b3349da) (Jeff Verkoeyen)
* [Add jazzy yaml.](https://github.com/material-motion/motion-animator-objc/commit/22e3bfc4a5bbe504f8f7dad3d41804257b50d28f) (Jeff Verkoeyen)

# 2.1.1

This patch release fixes issues with downstream bazel builds.
Expand Down
4 changes: 2 additions & 2 deletions MotionAnimator.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "MotionAnimator"
s.summary = "A Motion Animator creates performant, interruptible animations from motion specs."
s.version = "2.1.1"
s.version = "2.2.0"
s.authors = "The Material Motion Authors"
s.license = "Apache 2.0"
s.homepage = "https://github.com/material-motion/motion-animator-objc"
Expand All @@ -12,5 +12,5 @@ Pod::Spec.new do |s|
s.public_header_files = "src/*.h"
s.source_files = "src/*.{h,m,mm}", "src/private/*.{h,m,mm}"

s.dependency "MotionInterchange", "~> 1.2"
s.dependency "MotionInterchange", "~> 1.3"
end
2 changes: 1 addition & 1 deletion Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ post_install do |installer|
target.build_configurations.each do |configuration|
configuration.build_settings['SWIFT_VERSION'] = "3.0"
if target.name.start_with?("Motion")
configuration.build_settings['WARNING_CFLAGS'] ="$(inherited) -Wall -Wcast-align -Wconversion -Werror -Wextra -Wimplicit-atomic-properties -Wmissing-prototypes -Wno-sign-conversion -Wno-unused-parameter -Woverlength-strings -Wshadow -Wstrict-selector-match -Wundeclared-selector -Wunreachable-code"
configuration.build_settings['WARNING_CFLAGS'] ="$(inherited) -Wall -Wcast-align -Wconversion -Werror -Wextra -Wimplicit-atomic-properties -Wmissing-prototypes -Wno-sign-conversion -Wno-unused-parameter -Woverlength-strings -Wshadow -Wstrict-selector-match -Wundeclared-selector -Wunreachable-code -Wno-error=deprecated -Wno-error=deprecated-implementations"
end
end
end
Expand Down
12 changes: 6 additions & 6 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
PODS:
- CatalogByConvention (2.2.0)
- MotionAnimator (2.1.1):
- MotionInterchange (~> 1.2)
- MotionInterchange (1.2.0)
- MotionAnimator (2.2.0):
- MotionInterchange (~> 1.3)
- MotionInterchange (1.3.0)

DEPENDENCIES:
- CatalogByConvention
Expand All @@ -14,9 +14,9 @@ EXTERNAL SOURCES:

SPEC CHECKSUMS:
CatalogByConvention: 5df5831e48b8083b18570dcb804f20fd1c90694f
MotionAnimator: f306d8ee1a6600b039345c548cb543ebaa9cdec4
MotionInterchange: 499c98e7628a8a078905749734dbfedbfae54cca
MotionAnimator: 10d23fe5af75b53bfe43b1b046fc7052c52c870f
MotionInterchange: 988fc0011e4b806cc33f2fb4f9566f5eeb4159e8

PODFILE CHECKSUM: 3c50d819e57d8329e39f3f5677139bf93ac34b8b
PODFILE CHECKSUM: 3537bf01c11174928ac008c20fec4738722e96f3

COCOAPODS: 1.3.1
184 changes: 157 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@
[![Platform](https://img.shields.io/cocoapods/p/MotionAnimator.svg)](http://cocoadocs.org/docsets/MotionAnimator)
[![Docs](https://img.shields.io/cocoapods/metrics/doc-percent/MotionAnimator.svg)](http://cocoadocs.org/docsets/MotionAnimator)

This library turns [Motion Interchange](https://github.com/material-motion/motion-interchange-objc)
data structures into performant Core Animation animations using a lightweight animator object.
---

This library provides APIs that turn [Motion Interchange](https://github.com/material-motion/motion-interchange-objc)
**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:

<img src="assets/chip.gif" />

In the above example we're animating the expansion and collapse of a calendar event using the
following motion specification:
We might create a specification like so:

```objc
struct CalendarChipTiming {
Expand All @@ -38,9 +48,34 @@ typedef struct CalendarChipMotionSpec CalendarChipMotionSpec;
FOUNDATION_EXTERN struct CalendarChipMotionSpec CalendarChipSpec;
```

In our application logic, we first determine which motion timing to use and then we create an
instance of `MDMMotionAnimator`. The animator allows us to create animations with the given
motion timing.
With our implementation of the spec looking like so:

```objc
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 intend to use and then use the timings to animate our views with an
instance of `MDMMotionAnimator`:

```objc
CalendarChipTiming timing = _expanded ? CalendarChipSpec.expansion : CalendarChipSpec.collapse;
Expand All @@ -55,6 +90,18 @@ animator.shouldReverseValues = !_expanded;
...
```
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
commands:
git clone https://github.com/material-motion/motion-animator-objc.git
cd motion-animator-objc
pod install
open MotionAnimator.xcworkspace
## Installation
### Installation with CocoaPods
Expand Down Expand Up @@ -82,35 +129,118 @@ Import the framework:
You will now have access to all of the APIs.
## Example apps/unit tests
## Guides
Check out a local copy of the repo to access the Catalog application by running the following
commands:
- [How to make a spec from existing animations](#how-to-make-a-spec-from-existing-animations)
- [How to animate explicit layer properties](#how-to-animate-explicit-layer-properties)
- [How to animate like UIView](#how-to-animate-like-UIView)
- [How to animate a transition](#how-to-animate-a-transition)
- [How to animate an interruptible transition](#how-to-animate-an-interruptible-transition)
git clone https://github.com/material-motion/motion-animator-objc.git
cd motion-animator-objc
pod install
open MotionAnimator.xcworkspace
### How to make a spec from existing animations
## Guides
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.
1. [Architecture](#architecture)
2. [How to animate a transition](#how-to-animate-a-transition)
3. [How to animate an interruptible transition](#how-to-animate-an-interruptible-transition)
Consider a simple example of animating a view on and off-screen. Without a spec, our code might look
like so:
### Architecture
```objc
CGPoint before = dismissing ? onscreen : offscreen;
CGPoint after = dismissing ? offscreen : onscreen;
view.center = before;
[UIView animateWithDuration:0.5 animations:^{
view.center = after;
}];
```

`MDMMotionAnimator` is the primary API provided by this library. You can configure the animations
that an animator creates by modifying its configuration properties. When you're ready to add an
animation to a CALayer instance, call one of the `animate` method variants and an animation will
be added to the layer.
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:

This library depends on [MotionInterchange](https://github.com/material-motion/motion-interchange-objc)
in order to represent motion timing in a consistent fashion.
```objc
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];
```
### How to animate a transition
Now let's say we wrote the same code with a motion spec and animator:
> This guide assumes that you are animating a two state bi-directional transition.
```objc
static const MDMMotionTiming kMotionSpec = {
.duration = 0.5, .curve = _MDMSpring(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:

```objc
static const MDMMotionTiming kMotionSpec = {
.duration = 0.5, .curve = _MDMBezier(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**

- [Motion spec declaration](https://github.com/material-components/material-components-ios/blob/develop/components/ActivityIndicator/src/private/MDCActivityIndicatorMotionSpec.h)
- [Motion spec definition](https://github.com/material-components/material-components-ios/blob/develop/components/ActivityIndicator/src/private/MDCActivityIndicatorMotionSpec.m)
- [Motion spec usage](https://github.com/material-components/material-components-ios/blob/develop/components/ActivityIndicator/src/MDCActivityIndicator.m#L461)

**Material Components Progress View**

- [Motion spec declaration](https://github.com/material-components/material-components-ios/blob/develop/components/ProgressView/src/private/MDCProgressView%2BMotionSpec.h#L21)
- [Motion spec definition](https://github.com/material-components/material-components-ios/blob/develop/components/ProgressView/src/private/MDCProgressView%2BMotionSpec.m#L19)
- [Motion spec usage](https://github.com/material-components/material-components-ios/blob/develop/components/ProgressView/src/MDCProgressView.m#L155)

**Material Components Masked Transition**

- [Motion spec declaration](https://github.com/material-components/material-components-ios/blob/develop/components/MaskedTransition/src/private/MDCMaskedTransitionMotionSpec.h#L20)
- [Motion spec definition](https://github.com/material-components/material-components-ios/blob/develop/components/MaskedTransition/src/private/MDCMaskedTransitionMotionSpec.m#L23)
- [Motion spec usage](https://github.com/material-components/material-components-ios/blob/develop/components/MaskedTransition/src/MDCMaskedTransition.m#L183)

### 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.

```objc
[animator animateWithTiming:timing.chipHeight
toLayer:chipView.layer
withValues:@[ @(chipFrame.size.height), @(headerFrame.size.height) ]
keyPath:MDMKeyPathHeight];
```
### 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:
```objc
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.

Expand Down
2 changes: 1 addition & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ git_repository(
git_repository(
name = "motion_interchange_objc",
remote = "https://github.com/material-motion/motion-interchange-objc.git",
tag = "v1.2.0",
tag = "v1.3.0",
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
2AA864EDA683CEF5FAA721BE /* Pods_UnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DBE814C7B88BAD6337052DB /* Pods_UnitTests.framework */; };
660636021FACC24300C3DFB8 /* TimeScaleFactorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660636011FACC24300C3DFB8 /* TimeScaleFactorTests.swift */; };
6625876C1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6625876B1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift */; };
666FAA841D384A6B000363DA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 666FAA831D384A6B000363DA /* AppDelegate.swift */; };
666FAA8B1D384A6B000363DA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 666FAA8A1D384A6B000363DA /* Assets.xcassets */; };
666FAA8E1D384A6B000363DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 666FAA8C1D384A6B000363DA /* LaunchScreen.storyboard */; };
Expand Down Expand Up @@ -46,6 +47,7 @@
50D808A6F9E944D54276D32F /* Pods_MotionAnimatorCatalog.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MotionAnimatorCatalog.framework; sourceTree = BUILT_PRODUCTS_DIR; };
52820916F8FAA40E942A7333 /* Pods-UnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-UnitTests.release.xcconfig"; path = "../../../Pods/Target Support Files/Pods-UnitTests/Pods-UnitTests.release.xcconfig"; sourceTree = "<group>"; };
660636011FACC24300C3DFB8 /* TimeScaleFactorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeScaleFactorTests.swift; sourceTree = "<group>"; };
6625876B1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialVelocityTests.swift; sourceTree = "<group>"; };
666FAA801D384A6B000363DA /* MotionAnimatorCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MotionAnimatorCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; };
666FAA831D384A6B000363DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Catalog/AppDelegate.swift; sourceTree = "<group>"; };
666FAA8A1D384A6B000363DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -209,6 +211,7 @@
66FD99F91EE9FBBE00C53A82 /* MotionAnimatorTests.m */,
668726491EF04B4C00113675 /* MotionAnimatorTests.swift */,
66BF5A8E1FB0E4CB00E864F6 /* ImplicitAnimationTests.swift */,
6625876B1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift */,
660636011FACC24300C3DFB8 /* TimeScaleFactorTests.swift */,
);
path = unit;
Expand Down Expand Up @@ -485,6 +488,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6625876C1FB4DB9C00BC7DF1 /* InitialVelocityTests.swift in Sources */,
660636021FACC24300C3DFB8 /* TimeScaleFactorTests.swift in Sources */,
66BF5A8F1FB0E4CB00E864F6 /* ImplicitAnimationTests.swift in Sources */,
6687264A1EF04B4C00113675 /* MotionAnimatorTests.swift in Sources */,
Expand Down
4 changes: 1 addition & 3 deletions src/MDMMotionAnimator.m
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ - (void)animateWithTiming:(MDMMotionTiming)timing
animation.toValue = [values lastObject];

if (![animation.fromValue isEqual:animation.toValue]) {
if (self.additive) {
MDMMakeAnimationAdditive(animation);
}
MDMConfigureAnimation(animation, self.additive, timing);

if (timing.delay != 0) {
animation.beginTime = ([layer convertTime:CACurrentMediaTime() fromLayer:nil]
Expand Down
14 changes: 10 additions & 4 deletions src/private/CABasicAnimation+MotionAnimator.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
FOUNDATION_EXPORT
CABasicAnimation *MDMAnimationFromTiming(MDMMotionTiming timing, CGFloat timeScaleFactor);

// Attemps to configure the provided animation to be additive.
// Not all animation value types are supported. If an animation's value type was not supported,
// the animation will not be modified.
FOUNDATION_EXPORT void MDMMakeAnimationAdditive(CABasicAnimation *animation);
// Attempts to configure the provided animation to be additive and, if the animation is a spring
// animation, will extract the initial velocity from the timing and apply it to the animation.
//
// Not all animation value types support being additive. If an animation's value type was not
// supported, the animation's values will not be modified.
//
// If the from and to values of the animation match then the behavior is undefined.
FOUNDATION_EXPORT void MDMConfigureAnimation(CABasicAnimation *animation,
BOOL wantsAdditive,
MDMMotionTiming timing);
Loading

0 comments on commit 178fbc3

Please sign in to comment.