diff --git a/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj b/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj index c0139ca..899b7e7 100644 --- a/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj +++ b/examples/apps/Catalog/MotionInterchangeCatalog.xcodeproj/project.pbxproj @@ -7,14 +7,17 @@ objects = { /* Begin PBXBuildFile section */ + 660248AE1FD1EE78004C0147 /* MDMSpringTimingCurve.swift in Sources */ = {isa = PBXBuildFile; fileRef = 660248AC1FD1EE78004C0147 /* MDMSpringTimingCurve.swift */; }; 6619E1D91FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6619E1D81FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m */; }; 663ED7C51EDF1F0C0096B2A9 /* ExampleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C01EDF1F0C0096B2A9 /* ExampleViewController.swift */; }; 663ED7C61EDF1F0C0096B2A9 /* ExampleViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C11EDF1F0C0096B2A9 /* ExampleViews.swift */; }; 663ED7C71EDF1F0C0096B2A9 /* HexColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C21EDF1F0C0096B2A9 /* HexColor.swift */; }; 663ED7C81EDF1F0C0096B2A9 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C31EDF1F0C0096B2A9 /* Layout.swift */; }; 663ED7C91EDF1F0C0096B2A9 /* ModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED7C41EDF1F0C0096B2A9 /* ModalViewController.swift */; }; - 663ED8011EE628BA0096B2A9 /* MDMMotionCurveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663ED8001EE628BA0096B2A9 /* MDMMotionCurveTests.swift */; }; - 663ED8031EE6299A0096B2A9 /* MDMMotionCurveTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 663ED8021EE6299A0096B2A9 /* MDMMotionCurveTests.m */; }; + 664C8C531FD5A555004ED471 /* CAMediaTimingFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664C8C521FD5A555004ED471 /* CAMediaTimingFunctionTests.swift */; }; + 664C8C551FD5A7B7004ED471 /* MDMRepetitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664C8C541FD5A7B7004ED471 /* MDMRepetitionTests.swift */; }; + 664C8C571FD5A831004ED471 /* MDMRepetitionOverTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664C8C561FD5A831004ED471 /* MDMRepetitionOverTimeTests.swift */; }; + 664C8C591FD5A8AC004ED471 /* MDMAnimationTraitsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664C8C581FD5A8AC004ED471 /* MDMAnimationTraitsTests.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 */; }; @@ -47,14 +50,17 @@ 09CEA5DEA01BA723D08D84E6 /* 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 = ""; }; 2DE76D4D35953D836F578CDE /* Pods-MotionInterchangeCatalog.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MotionInterchangeCatalog.debug.xcconfig"; path = "../../../Pods/Target Support Files/Pods-MotionInterchangeCatalog/Pods-MotionInterchangeCatalog.debug.xcconfig"; sourceTree = ""; }; 4AAB8EBB088513D48896641A /* Pods-MotionInterchangeCatalog.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MotionInterchangeCatalog.release.xcconfig"; path = "../../../Pods/Target Support Files/Pods-MotionInterchangeCatalog/Pods-MotionInterchangeCatalog.release.xcconfig"; sourceTree = ""; }; + 660248AC1FD1EE78004C0147 /* MDMSpringTimingCurve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDMSpringTimingCurve.swift; sourceTree = ""; }; 6619E1D81FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MDMModalMovementTimingTests.m; sourceTree = ""; }; 663ED7C01EDF1F0C0096B2A9 /* ExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleViewController.swift; sourceTree = ""; }; 663ED7C11EDF1F0C0096B2A9 /* ExampleViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleViews.swift; sourceTree = ""; }; 663ED7C21EDF1F0C0096B2A9 /* HexColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HexColor.swift; sourceTree = ""; }; 663ED7C31EDF1F0C0096B2A9 /* Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = ""; }; 663ED7C41EDF1F0C0096B2A9 /* ModalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalViewController.swift; sourceTree = ""; }; - 663ED8001EE628BA0096B2A9 /* MDMMotionCurveTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MDMMotionCurveTests.swift; sourceTree = ""; }; - 663ED8021EE6299A0096B2A9 /* MDMMotionCurveTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDMMotionCurveTests.m; sourceTree = ""; }; + 664C8C521FD5A555004ED471 /* CAMediaTimingFunctionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAMediaTimingFunctionTests.swift; sourceTree = ""; }; + 664C8C541FD5A7B7004ED471 /* MDMRepetitionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MDMRepetitionTests.swift; sourceTree = ""; }; + 664C8C561FD5A831004ED471 /* MDMRepetitionOverTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MDMRepetitionOverTimeTests.swift; sourceTree = ""; }; + 664C8C581FD5A8AC004ED471 /* MDMAnimationTraitsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MDMAnimationTraitsTests.swift; sourceTree = ""; }; 666FAA801D384A6B000363DA /* MotionInterchangeCatalog.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MotionInterchangeCatalog.app; sourceTree = BUILT_PRODUCTS_DIR; }; 666FAA831D384A6B000363DA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = Catalog/AppDelegate.swift; sourceTree = ""; }; 666FAA8A1D384A6B000363DA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -162,9 +168,12 @@ 666FAA971D384A6B000363DA /* tests */ = { isa = PBXGroup; children = ( - 663ED8001EE628BA0096B2A9 /* MDMMotionCurveTests.swift */, - 663ED8021EE6299A0096B2A9 /* MDMMotionCurveTests.m */, + 664C8C521FD5A555004ED471 /* CAMediaTimingFunctionTests.swift */, + 664C8C581FD5A8AC004ED471 /* MDMAnimationTraitsTests.swift */, 6619E1D81FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m */, + 664C8C541FD5A7B7004ED471 /* MDMRepetitionTests.swift */, + 664C8C561FD5A831004ED471 /* MDMRepetitionOverTimeTests.swift */, + 660248AC1FD1EE78004C0147 /* MDMSpringTimingCurve.swift */, ); name = tests; path = ../../../tests/unit; @@ -478,9 +487,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 663ED8031EE6299A0096B2A9 /* MDMMotionCurveTests.m in Sources */, - 663ED8011EE628BA0096B2A9 /* MDMMotionCurveTests.swift in Sources */, + 664C8C531FD5A555004ED471 /* CAMediaTimingFunctionTests.swift in Sources */, + 660248AE1FD1EE78004C0147 /* MDMSpringTimingCurve.swift in Sources */, 6619E1D91FA0ED0300F3AB25 /* MDMModalMovementTimingTests.m in Sources */, + 664C8C571FD5A831004ED471 /* MDMRepetitionOverTimeTests.swift in Sources */, + 664C8C591FD5A8AC004ED471 /* MDMAnimationTraitsTests.swift in Sources */, + 664C8C551FD5A7B7004ED471 /* MDMRepetitionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/scripts/v1_to_v2.sh b/scripts/v1_to_v2.sh new file mode 100755 index 0000000..b601faa --- /dev/null +++ b/scripts/v1_to_v2.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# Copyright 2017-present The Material Motion Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Migration script from v1 to v2 interchange APIs. + +if [ "$#" -ne 1 ]; then + echo "Usage: $(basename $0) " + exit 1 +fi + +search_path="$1" + +replace_objc() { + find "$search_path" -type f -name "*.h" | xargs sed -i '' "$1" + find "$search_path" -type f -name "*.m" | xargs sed -i '' "$1" +} + +replace_swift() { + find "$search_path" -type f -name "*.swift" | xargs sed -i '' "$1" +} + +replace_all() { + replace_objc "$1" + replace_swift "$1" +} + +replace_all "s/timing.curve/traits.timingCurve/g" +replace_all "s/traits.curve/traits.timingCurve/g" +replace_objc "s/MDMMotionTiming/MDMAnimationTraits */g" +replace_swift "s/MotionTiming/MDMAnimationTraits/g" diff --git a/src/CAMediaTimingFunction+MDMTimingCurve.h b/src/CAMediaTimingFunction+MDMTimingCurve.h new file mode 100644 index 0000000..e140d0b --- /dev/null +++ b/src/CAMediaTimingFunction+MDMTimingCurve.h @@ -0,0 +1,44 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import +#import + +#import "MDMTimingCurve.h" + +// A CAMediaTimingFunction is a timing curve - we simply define its conformity to our protocol here. +@interface CAMediaTimingFunction () +@end + +@interface CAMediaTimingFunction (MotionInterchangeExtension) + +/** + Returns a instance of the timing function with its control points reversed. + */ +- (nonnull CAMediaTimingFunction *)mdm_reversed; + +/** + Returns the first control point of the timing function. + */ +@property(nonatomic, assign, readonly) CGPoint mdm_point1; + +/** + Returns the second control point of the timing function. + */ +@property(nonatomic, assign, readonly) CGPoint mdm_point2; + +@end + diff --git a/src/CAMediaTimingFunction+MDMTimingCurve.m b/src/CAMediaTimingFunction+MDMTimingCurve.m new file mode 100644 index 0000000..f344f17 --- /dev/null +++ b/src/CAMediaTimingFunction+MDMTimingCurve.m @@ -0,0 +1,49 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "CAMediaTimingFunction+MDMTimingCurve.h" + +@implementation CAMediaTimingFunction (MotionInterchangeExtension) + +- (CAMediaTimingFunction *)mdm_reversed { + float pt1[2]; + float pt2[2]; + [self getControlPointAtIndex:1 values:pt1]; + [self getControlPointAtIndex:2 values:pt2]; + + float reversedPt1[2]; + float reversedPt2[2]; + reversedPt1[0] = 1 - pt2[0]; + reversedPt1[1] = 1 - pt2[1]; + reversedPt2[0] = 1 - pt1[0]; + reversedPt2[1] = 1 - pt1[1]; + return [CAMediaTimingFunction functionWithControlPoints:reversedPt1[0] :reversedPt1[1] + :reversedPt2[0] :reversedPt2[1]]; +} + +- (CGPoint)mdm_point1 { + float point[2]; + [self getControlPointAtIndex:1 values:point]; + return CGPointMake(point[0], point[1]); +} + +- (CGPoint)mdm_point2 { + float point[2]; + [self getControlPointAtIndex:2 values:point]; + return CGPointMake(point[0], point[1]); +} + +@end diff --git a/src/MDMAnimationTraits.h b/src/MDMAnimationTraits.h new file mode 100644 index 0000000..3eefaac --- /dev/null +++ b/src/MDMAnimationTraits.h @@ -0,0 +1,115 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import +#import + +#import "MDMRepetitionTraits.h" +#import "MDMTimingCurve.h" + +/** + A generic representation of animation traits. + */ +@interface MDMAnimationTraits: NSObject + +/** + Initializes the instance with the provided duration and kCAMediaTimingFunctionEaseInEaseOut timing + curve. + + @param duration The animation will occur over this length of time, in seconds. + */ +- (nonnull instancetype)initWithDuration:(NSTimeInterval)duration; + +/** + Initializes the instance with the provided duration, delay, and + kCAMediaTimingFunctionEaseInEaseOut timing curve. + + @param delay The amount of time, in seconds, to wait before starting the animation. + @param duration The animation will occur over this length of time, in seconds, after the delay time + has passed. + */ +- (nonnull instancetype)initWithDelay:(NSTimeInterval)delay duration:(NSTimeInterval)duration; + +/** + Initializes the instance with the provided duration, delay, and timing curve. + + @param delay The amount of time, in seconds, to wait before starting the animation. + @param duration The animation will occur over this length of time, in seconds, after the delay time + has passed. + @param timingCurve If provided, defines the acceleration timing for the animation. If nil, the + animation will be treated as instant and the duration/delay will be ignored. + */ +- (nonnull instancetype)initWithDelay:(NSTimeInterval)delay + duration:(NSTimeInterval)duration + timingCurve:(nullable id)timingCurve; + +/** + Initializes an animation trait with the provided timing curve, duration, delay, and repetition. + + @param duration The animation will occur over this length of time, in seconds, after the delay time + has passed. + @param delay The amount of time, in seconds, to wait before starting the animation. + @param timingCurve If provided, defines the acceleration timing for the animation. If nil, the + animation will be treated as instant and the duration/delay will be ignored. + @param repetition The repetition traits of the animation. Most often an instance of MDMRepetition + or MDMRepetitionOverTime. If nil, the animation will not repeat. + */ +- (nonnull instancetype)initWithDelay:(NSTimeInterval)delay + duration:(NSTimeInterval)duration + timingCurve:(nullable id)timingCurve + repetition:(nullable id)repetition + NS_DESIGNATED_INITIALIZER; + +#pragma mark - Traits + +/** + The amount of time, in seconds, before this animation's value interpolation should begin. + */ +@property(nonatomic, assign, readonly) NSTimeInterval delay; + +/** + The amount of time, in seconds, over which this animation should interpolate between its values. + */ +@property(nonatomic, assign, readonly) NSTimeInterval duration; + +/** + The velocity and acceleration of the animation over time. + */ +@property(nonatomic, strong, nullable, readonly) id timingCurve; + +/** + The repetition characteristics of the animation. + */ +@property(nonatomic, strong, nullable, readonly) id repetition; + +#pragma mark - Unavailable + +/** + Unavailable. + */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +@end + +@interface MDMAnimationTraits (SystemTraits) + +/** + Animation traits for an iOS modal presentation slide animation. + */ +@property(nonatomic, class, strong, nonnull, readonly) MDMAnimationTraits *systemModalMovement; + +@end + diff --git a/src/MDMAnimationTraits.m b/src/MDMAnimationTraits.m new file mode 100644 index 0000000..99bf382 --- /dev/null +++ b/src/MDMAnimationTraits.m @@ -0,0 +1,71 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDMAnimationTraits.h" + +#import "CAMediaTimingFunction+MDMTimingCurve.h" +#import "MDMSpringTimingCurve.h" + +@implementation MDMAnimationTraits + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (nonnull instancetype)initWithDuration:(NSTimeInterval)duration { + return [self initWithDelay:0 duration:duration]; +} + +- (instancetype)initWithDelay:(NSTimeInterval)delay duration:(NSTimeInterval)duration { + CAMediaTimingFunction *easeInOut = + [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + return [self initWithDelay:delay duration:duration timingCurve:easeInOut]; +} + +- (instancetype)initWithDelay:(NSTimeInterval)delay + duration:(NSTimeInterval)duration + timingCurve:(id)timingCurve { + return [self initWithDelay:delay duration:duration timingCurve:timingCurve repetition:nil]; +} + +- (instancetype)initWithDelay:(NSTimeInterval)delay + duration:(NSTimeInterval)duration + timingCurve:(id)timingCurve + repetition:(id)repetition { + self = [super init]; + if (self) { + _duration = duration; + _delay = delay; + _timingCurve = timingCurve; + _repetition = repetition; + } + return self; +} + +@end + +@implementation MDMAnimationTraits (SystemTraits) + ++ (MDMAnimationTraits *)systemModalMovement { + MDMSpringTimingCurve *timingCurve = [[MDMSpringTimingCurve alloc] initWithMass:3 + tension:1000 + friction:500]; + return [[MDMAnimationTraits alloc] initWithDelay:0 duration:0.500 timingCurve:timingCurve]; +} + +@end + diff --git a/src/MDMMotionCurve.h b/src/MDMMotionCurve.h deleted file mode 100644 index 0b233eb..0000000 --- a/src/MDMMotionCurve.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - Copyright 2017-present The Material Motion Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import -#import -#import - -/** - The possible kinds of motion curves that can be used to describe an animation. - */ -typedef NS_ENUM(NSUInteger, MDMMotionCurveType) { - /** - The value will be instantly set with no animation. - */ - MDMMotionCurveTypeInstant, - - /** - The value will be animated using a cubic bezier curve to model its velocity. - */ - MDMMotionCurveTypeBezier, - - /** - The value will be animated using a spring simulation. - - A spring will treat the duration property of the motion timing as a suggestion and may choose to - ignore it altogether. - */ - MDMMotionCurveTypeSpring, - - /** - The default curve will be used. - */ - MDMMotionCurveTypeDefault __deprecated_enum_msg("Use MDMMotionCurveTypeBezier instead."), - -} NS_SWIFT_NAME(MotionCurveType); - -/** - A generalized representation of a motion curve. - */ -struct MDMMotionCurve { - /** - The type defines how to interpret the data values. - */ - MDMMotionCurveType type; - - /** - The data values corresponding with this curve. - */ - CGFloat data[4]; -} NS_SWIFT_NAME(MotionCurve); -typedef struct MDMMotionCurve MDMMotionCurve; - -/** - Creates a bezier motion curve with the provided control points. - - A cubic bezier has four control points in total. We assume that the first control point is 0, 0 and - the last control point is 1, 1. This method requires that you provide the second and third control - points. - - See the documentation for CAMediaTimingFunction for more information. - */ -// clang-format off -FOUNDATION_EXTERN -MDMMotionCurve MDMMotionCurveMakeBezier(CGFloat p1x, CGFloat p1y, CGFloat p2x, CGFloat p2y) - NS_SWIFT_NAME(MotionCurveMakeBezier(p1x:p1y:p2x:p2y:)); -// clang-format on - -// clang-format off -FOUNDATION_EXTERN -MDMMotionCurve MDMMotionCurveFromTimingFunction(CAMediaTimingFunction * _Nonnull timingFunction) - NS_SWIFT_NAME(MotionCurve(fromTimingFunction:)); -// clang-format on - -/** - Creates a spring curve with the provided configuration. - - Tension and friction map to Core Animation's stiffness and damping, respectively. - - See the documentation for CASpringAnimation for more information. - */ -// clang-format off -FOUNDATION_EXTERN MDMMotionCurve MDMMotionCurveMakeSpring(CGFloat mass, - CGFloat tension, - CGFloat friction) - NS_SWIFT_NAME(MotionCurveMakeSpring(mass:tension:friction:)); -// clang-format on - -/** - Creates a spring curve with the provided configuration. - - Tension and friction map to Core Animation's stiffness and damping, respectively. - - See the documentation for CASpringAnimation for more information. - */ -// clang-format off -FOUNDATION_EXTERN -MDMMotionCurve MDMMotionCurveMakeSpringWithInitialVelocity(CGFloat mass, - CGFloat tension, - CGFloat friction, - CGFloat initialVelocity) - NS_SWIFT_NAME(MotionCurveMakeSpring(mass:tension:friction:initialVelocity:)); -// clang-format on - -/** - For cubic bezier curves, returns a reversed cubic bezier curve. For all other curve types, a copy - of the original curve is returned. - */ -// clang-format off -FOUNDATION_EXTERN MDMMotionCurve MDMMotionCurveReversedBezier(MDMMotionCurve motionCurve) - NS_SWIFT_NAME(MotionCurveReversedBezier(fromMotionCurve:)); -// clang-format on - -/** - Named indices for the bezier motion curve's data array. - */ -typedef NS_ENUM(NSUInteger, MDMBezierMotionCurveDataIndex) { - MDMBezierMotionCurveDataIndexP1X, - MDMBezierMotionCurveDataIndexP1Y, - MDMBezierMotionCurveDataIndexP2X, - MDMBezierMotionCurveDataIndexP2Y -} NS_SWIFT_NAME(BezierMotionCurveDataIndex); - -/** - Named indices for the spring motion curve's data array. - */ -typedef NS_ENUM(NSUInteger, MDMSpringMotionCurveDataIndex) { - MDMSpringMotionCurveDataIndexMass, - MDMSpringMotionCurveDataIndexTension, - MDMSpringMotionCurveDataIndexFriction, - - /** - The initial velocity of the animation. - - A value of zero indicates no initial velocity. - A positive value indicates movement toward the destination. - A negative value indicates movement away from the destination. - - The value's units are dependent on the context and the value being animated. - */ - MDMSpringMotionCurveDataIndexInitialVelocity -} NS_SWIFT_NAME(SpringMotionCurveDataIndex); - -// Objective-C-specific macros - -#define _MDMBezier(p1x, p1y, p2x, p2y) \ - ((MDMMotionCurve) { \ - .type = MDMMotionCurveTypeBezier, \ - .data = { p1x, \ - p1y, \ - p2x, \ - p2y } \ - }) - -#define _MDMSpring(mass, tension, friction) \ - ((MDMMotionCurve) { \ - .type = MDMMotionCurveTypeSpring, \ - .data = { mass, \ - tension, \ - friction } \ - }) - -#define _MDMSpringWithInitialVelocity(mass, tension, friction, initialVelocity) \ - ((MDMMotionCurve) { \ - .type = MDMMotionCurveTypeSpring, \ - .data = { mass, \ - tension, \ - friction, \ - initialVelocity } \ - }) - -/** - A linear bezier motion curve. - */ -#define MDMLinearMotionCurve _MDMBezier(0, 0, 1, 1) - -/** - Timing information for an iOS modal presentation slide animation. - */ -#define MDMModalMovementTiming { \ - .delay = 0.000, .duration = 0.500, .curve = _MDMSpring(3, 1000, 500) \ -} diff --git a/src/MDMMotionCurve.m b/src/MDMMotionCurve.m deleted file mode 100644 index 2cc57c8..0000000 --- a/src/MDMMotionCurve.m +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2017-present The Material Motion Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import "MDMMotionCurve.h" - -MDMMotionCurve MDMMotionCurveMakeBezier(CGFloat p1x, CGFloat p1y, CGFloat p2x, CGFloat p2y) { - return _MDMBezier(p1x, p1y, p2x, p2y); -} - -MDMMotionCurve MDMMotionCurveMakeSpring(CGFloat mass, CGFloat tension, CGFloat friction) { - return MDMMotionCurveMakeSpringWithInitialVelocity(mass, tension, friction, 0); -} - -MDMMotionCurve MDMMotionCurveMakeSpringWithInitialVelocity(CGFloat mass, - CGFloat tension, - CGFloat friction, - CGFloat initialVelocity) { - return _MDMSpringWithInitialVelocity(mass, tension, friction, initialVelocity); -} - -MDMMotionCurve MDMMotionCurveFromTimingFunction(CAMediaTimingFunction *timingFunction) { - float pt1[2]; - float pt2[2]; - [timingFunction getControlPointAtIndex:1 values:pt1]; - [timingFunction getControlPointAtIndex:2 values:pt2]; - return MDMMotionCurveMakeBezier(pt1[0], pt1[1], pt2[0], pt2[1]); -} - -MDMMotionCurve MDMMotionCurveReversedBezier(MDMMotionCurve motionCurve) { - MDMMotionCurve reversed = motionCurve; - if (motionCurve.type == MDMMotionCurveTypeBezier) { - reversed.data[0] = 1 - motionCurve.data[2]; - reversed.data[1] = 1 - motionCurve.data[3]; - reversed.data[2] = 1 - motionCurve.data[0]; - reversed.data[3] = 1 - motionCurve.data[1]; - } - return reversed; -} diff --git a/src/MDMMotionRepetition.h b/src/MDMMotionRepetition.h deleted file mode 100644 index 94fc266..0000000 --- a/src/MDMMotionRepetition.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - Copyright 2017-present The Material Motion Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import -#import - -/** - The possible kinds of repetition that can be used to describe an animation. - */ -typedef NS_ENUM(NSUInteger, MDMMotionRepetitionType) { - /** - The animation will be not be repeated. - */ - MDMMotionRepetitionTypeNone, - - /** - The animation will be repeated a given number of times. - */ - MDMMotionRepetitionTypeCount, - - /** - The animation will be repeated for a given number of seconds. - */ - MDMMotionRepetitionTypeDuration, - -} NS_SWIFT_NAME(MotionReptitionType); - -/** - A generalized representation of a motion curve. - */ -struct MDMMotionRepetition { - /** - The type defines how to interpret the amount. - */ - MDMMotionRepetitionType type; - - /** - The amount of repetition. - */ - double amount; - - /** - Whether the animation should animate backwards after animating forwards. - */ - BOOL autoreverses; - -} NS_SWIFT_NAME(MotionRepetition); -typedef struct MDMMotionRepetition MDMMotionRepetition; - -// Objective-C-specific macros - -#define _MDMNoRepetition \ - (MDMMotionRepetition) { \ - .type = MDMMotionRepetitionTypeNone, \ - .amount = 0, \ - .autoreverses = false \ - } diff --git a/src/MDMMotionTiming.h b/src/MDMMotionTiming.h deleted file mode 100644 index b225570..0000000 --- a/src/MDMMotionTiming.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2017-present The Material Motion Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import -#import - -#import "MDMMotionCurve.h" -#import "MDMMotionRepetition.h" - -/** - A representation of timing for an animation. - */ -struct MDMMotionTiming { - /** - The amount of time, in seconds, before this animation's value interpolation should begin. - */ - CFTimeInterval delay; - - /** - The amount of time, in seconds, over which this animation should interpolate between its values. - */ - CFTimeInterval duration; - - /** - The velocity and acceleration of the animation over time. - */ - MDMMotionCurve curve; - - /** - The repetition characteristics of the animation. - */ - MDMMotionRepetition repetition; - -} NS_SWIFT_NAME(MotionTiming); -typedef struct MDMMotionTiming MDMMotionTiming; diff --git a/src/MDMRepetition.h b/src/MDMRepetition.h new file mode 100644 index 0000000..523082f --- /dev/null +++ b/src/MDMRepetition.h @@ -0,0 +1,60 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MDMRepetitionTraits.h" + +/** + Represents repetition that repeats a specific number of times. + */ +@interface MDMRepetition: NSObject + +/** + Initializes the instance with the given number of repetitions. + + Autoreversing is disabled. + + @param numberOfRepetitions May be fractional. Initializing with greatestFiniteMagnitude will cause + the animation to repeat forever. + */ +- (nonnull instancetype)initWithNumberOfRepetitions:(double)numberOfRepetitions; + +/** + Initializes the instance with the given number of repetitions and autoreversal behavior. + + @param numberOfRepetitions May be fractional. Initializing with greatestFiniteMagnitude will cause + the animation to repeat forever. + @param autoreverses Whether the animation should animate backwards after animating forwards. + */ +- (nonnull instancetype)initWithNumberOfRepetitions:(double)numberOfRepetitions + autoreverses:(BOOL)autoreverses + NS_DESIGNATED_INITIALIZER; + +#pragma mark - Traits + +/** + The number of repetitions that will occur before this animation stops repeating. + */ +@property(nonatomic, assign, readonly) double numberOfRepetitions; + +/** + Unavailable. + */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +@end + diff --git a/src/MDMRepetition.m b/src/MDMRepetition.m new file mode 100644 index 0000000..e3d5a1c --- /dev/null +++ b/src/MDMRepetition.m @@ -0,0 +1,43 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDMRepetition.h" + +@implementation MDMRepetition + +@synthesize autoreverses = _autoreverses; + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithNumberOfRepetitions:(double)numberOfRepetitions { + return [self initWithNumberOfRepetitions:numberOfRepetitions autoreverses:NO]; +} + +- (instancetype)initWithNumberOfRepetitions:(double)numberOfRepetitions + autoreverses:(BOOL)autoreverses { + self = [super init]; + if (self) { + _numberOfRepetitions = numberOfRepetitions; + _autoreverses = autoreverses; + } + return self; +} + +@end + diff --git a/src/MDMRepetitionOverTime.h b/src/MDMRepetitionOverTime.h new file mode 100644 index 0000000..507b422 --- /dev/null +++ b/src/MDMRepetitionOverTime.h @@ -0,0 +1,55 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +#import "MDMRepetitionTraits.h" + +/** + Represents repetition that repeats until a specific duration has passed. + */ +@interface MDMRepetitionOverTime: NSObject + +/** + Initializes the instance with the given duration. + + @param duration The amount of time, in seconds, over which the animation will repeat. + */ +- (nonnull instancetype)initWithDuration:(double)duration; + +/** + Initializes the instance with the given duration and autoreversal behavior. + + @param duration The amount of time, in seconds, over which the animation will repeat. + @param autoreverses Whether the animation should animate backwards after animating forwards. + */ +- (nonnull instancetype)initWithDuration:(double)duration autoreverses:(BOOL)autoreverses + NS_DESIGNATED_INITIALIZER; + +#pragma mark - Traits + +/** + The amount of time, in seconds, that will pass before this animation stops repeating. + */ +@property(nonatomic, assign, readonly) double duration; + +/** + Unavailable. + */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +@end + diff --git a/src/MDMRepetitionOverTime.m b/src/MDMRepetitionOverTime.m new file mode 100644 index 0000000..55cdcab --- /dev/null +++ b/src/MDMRepetitionOverTime.m @@ -0,0 +1,42 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDMRepetitionOverTime.h" + +@implementation MDMRepetitionOverTime + +@synthesize autoreverses = _autoreverses; + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithDuration:(double)duration { + return [self initWithDuration:duration autoreverses:NO]; +} + +- (instancetype)initWithDuration:(double)duration autoreverses:(BOOL)autoreverses { + self = [super init]; + if (self) { + _duration = duration; + _autoreverses = autoreverses; + } + return self; +} + +@end + diff --git a/src/MDMRepetitionTraits.h b/src/MDMRepetitionTraits.h new file mode 100644 index 0000000..211f227 --- /dev/null +++ b/src/MDMRepetitionTraits.h @@ -0,0 +1,30 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import + +/** + A generalized representation of a repetition traits. + */ +@protocol MDMRepetitionTraits + +/** + Whether the animation should animate backwards after animating forwards. + */ +@property(nonatomic, assign, readonly) BOOL autoreverses; + +@end + diff --git a/src/MDMSpringTimingCurve.h b/src/MDMSpringTimingCurve.h new file mode 100644 index 0000000..30f7ca9 --- /dev/null +++ b/src/MDMSpringTimingCurve.h @@ -0,0 +1,95 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import +#import + +#import "MDMTimingCurve.h" + +/** + A timing curve that represents the motion of a single-dimensional attached spring. + */ +@interface MDMSpringTimingCurve: NSObject + +/** + Initializes the timing curve with the given parameters and an initial velocity of zero. + + @param mass The mass of the spring simulation. Affects the animation's momentum. + @param tension The tension of the spring simulation. Affects how quickly the animation moves + toward its destination. + @param friction The friction of the spring simulation. Affects how quickly the animation starts + and stops. + */ +- (nonnull instancetype)initWithMass:(CGFloat)mass + tension:(CGFloat)tension + friction:(CGFloat)friction; + +/** + Initializes the timing curve with the given parameters. + + @param mass The mass of the spring simulation. Affects the animation's momentum. + @param tension The tension of the spring simulation. Affects how quickly the animation moves + toward its destination. + @param friction The friction of the spring simulation. Affects how quickly the animation starts + and stops. + @param initialVelocity The initial velocity of the spring simulation. Measured in units of + translation per second. For example, if the property being animated is positional, then this value + is in screen units per second. + */ +- (nonnull instancetype)initWithMass:(CGFloat)mass + tension:(CGFloat)tension + friction:(CGFloat)friction + initialVelocity:(CGFloat)initialVelocity + NS_DESIGNATED_INITIALIZER; + +#pragma mark - Traits + +/** + The mass of the spring simulation. + + Affects the animation's momentum. This is usually 1. + */ +@property(nonatomic, assign, readonly) CGFloat mass; + +/** + The tension of the spring simulation. + + Affects how quickly the animation moves toward its destination. + */ +@property(nonatomic, assign, readonly) CGFloat tension; + +/** + The friction of the spring simulation. + + Affects how quickly the animation starts and stops. + */ +@property(nonatomic, assign, readonly) CGFloat friction; + +/** + The initial velocity of the spring simulation. + + Measured in units of translation per second. + */ +@property(nonatomic, assign, readonly) CGFloat initialVelocity; + +/** + Unavailable. + */ +- (nonnull instancetype)init NS_UNAVAILABLE; + +@end + + diff --git a/src/MDMSpringTimingCurve.m b/src/MDMSpringTimingCurve.m new file mode 100644 index 0000000..138a556 --- /dev/null +++ b/src/MDMSpringTimingCurve.m @@ -0,0 +1,45 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import "MDMSpringTimingCurve.h" + +@implementation MDMSpringTimingCurve + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithMass:(CGFloat)mass tension:(CGFloat)tension friction:(CGFloat)friction { + return [self initWithMass:mass tension:tension friction:friction initialVelocity:0]; +} + +- (instancetype)initWithMass:(CGFloat)mass + tension:(CGFloat)tension + friction:(CGFloat)friction + initialVelocity:(CGFloat)initialVelocity { + self = [super init]; + if (self) { + _mass = mass; + _tension = tension; + _friction = friction; + _initialVelocity = initialVelocity; + } + return self; +} + +@end + diff --git a/src/MDMTimingCurve.h b/src/MDMTimingCurve.h new file mode 100644 index 0000000..3a628ec --- /dev/null +++ b/src/MDMTimingCurve.h @@ -0,0 +1,24 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#import +#import + +/** + A generalized representation of a timing curve. + */ +@protocol MDMTimingCurve +@end diff --git a/src/MotionInterchange.h b/src/MotionInterchange.h index 33e8de8..540e559 100644 --- a/src/MotionInterchange.h +++ b/src/MotionInterchange.h @@ -14,6 +14,10 @@ limitations under the License. */ -#import "MDMMotionCurve.h" -#import "MDMMotionRepetition.h" -#import "MDMMotionTiming.h" +#import "CAMediaTimingFunction+MDMTimingCurve.h" +#import "MDMAnimationTraits.h" +#import "MDMRepetitionTraits.h" +#import "MDMRepetition.h" +#import "MDMRepetitionOverTime.h" +#import "MDMTimingCurve.h" +#import "MDMSpringTimingCurve.h" diff --git a/tests/unit/CAMediaTimingFunctionTests.swift b/tests/unit/CAMediaTimingFunctionTests.swift new file mode 100644 index 0000000..ae953ec --- /dev/null +++ b/tests/unit/CAMediaTimingFunctionTests.swift @@ -0,0 +1,39 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import XCTest +import MotionInterchange + +class CAMediaTimingFunctionTests: XCTestCase { + func testReversalAlgorithm() { + let curve = CAMediaTimingFunction(controlPoints: 0.1, 0.2, 0.3, 0.4) + let reversed = curve.mdm_reversed() + XCTAssertEqualWithAccuracy(curve.mdm_point1.x, 1 - reversed.mdm_point2.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.mdm_point1.y, 1 - reversed.mdm_point2.y, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.mdm_point2.x, 1 - reversed.mdm_point1.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.mdm_point2.y, 1 - reversed.mdm_point1.y, accuracy: 0.001) + } + + func testReversingBezierCurveTwiceGivesSameResult() { + let curve = CAMediaTimingFunction(controlPoints: 0.1, 0.2, 0.3, 0.4) + let reversed = curve.mdm_reversed() + let reversedAgain = reversed.mdm_reversed() + XCTAssertEqualWithAccuracy(curve.mdm_point1.x, reversedAgain.mdm_point1.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.mdm_point1.y, reversedAgain.mdm_point1.y, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.mdm_point2.x, reversedAgain.mdm_point2.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.mdm_point2.y, reversedAgain.mdm_point2.y, accuracy: 0.001) + } +} diff --git a/tests/unit/MDMAnimationTraitsTests.swift b/tests/unit/MDMAnimationTraitsTests.swift new file mode 100644 index 0000000..8755967 --- /dev/null +++ b/tests/unit/MDMAnimationTraitsTests.swift @@ -0,0 +1,113 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import XCTest +import MotionInterchange + +class MDMAnimationTraitsTests: XCTestCase { + + func testInitializerValuesWithDuration() { + let traits = MDMAnimationTraits(duration: 0.5) + + XCTAssertEqualWithAccuracy(traits.duration, 0.5, accuracy: 0.001) + XCTAssertEqualWithAccuracy(traits.delay, 0, accuracy: 0.001) + XCTAssertTrue(traits.timingCurve is CAMediaTimingFunction) + if let timingCurve = traits.timingCurve as? CAMediaTimingFunction { + let easeInOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.x, easeInOut.mdm_point1.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.y, easeInOut.mdm_point1.y, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.x, easeInOut.mdm_point2.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.y, easeInOut.mdm_point2.y, accuracy: 0.001) + } + XCTAssertNil(traits.repetition) + } + + func testInitializerValuesWithDurationDelay() { + let traits = MDMAnimationTraits(delay: 0.2, duration: 0.5) + + XCTAssertEqualWithAccuracy(traits.duration, 0.5, accuracy: 0.001) + XCTAssertEqualWithAccuracy(traits.delay, 0.2, accuracy: 0.001) + XCTAssertTrue(traits.timingCurve is CAMediaTimingFunction) + if let timingCurve = traits.timingCurve as? CAMediaTimingFunction { + let easeInOut = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.x, easeInOut.mdm_point1.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.y, easeInOut.mdm_point1.y, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.x, easeInOut.mdm_point2.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.y, easeInOut.mdm_point2.y, accuracy: 0.001) + } + XCTAssertNil(traits.repetition) + } + + func testInitializerValuesWithDurationDelayNilTimingCurve() { + let traits = MDMAnimationTraits(delay: 0.2, duration: 0.5, timingCurve: nil) + + XCTAssertEqualWithAccuracy(traits.duration, 0.5, accuracy: 0.001) + XCTAssertEqualWithAccuracy(traits.delay, 0.2, accuracy: 0.001) + XCTAssertNil(traits.timingCurve) + XCTAssertNil(traits.repetition) + } + + func testInitializerValuesWithDurationDelayLinearTimingCurve() { + let linear = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) + let traits = MDMAnimationTraits(delay: 0.2, duration: 0.5, timingCurve: linear) + + XCTAssertEqualWithAccuracy(traits.duration, 0.5, accuracy: 0.001) + XCTAssertEqualWithAccuracy(traits.delay, 0.2, accuracy: 0.001) + XCTAssertTrue(traits.timingCurve is CAMediaTimingFunction) + if let timingCurve = traits.timingCurve as? CAMediaTimingFunction { + XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.x, linear.mdm_point1.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point1.y, linear.mdm_point1.y, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.x, linear.mdm_point2.x, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.mdm_point2.y, linear.mdm_point2.y, accuracy: 0.001) + } + XCTAssertNil(traits.repetition) + } + + func testInitializerValuesWithDurationDelaySpringTimingCurve() { + let spring = MDMSpringTimingCurve(mass: 0.7, tension: 0.8, friction: 0.9) + let traits = MDMAnimationTraits(delay: 0.2, duration: 0.5, timingCurve: spring) + + XCTAssertEqualWithAccuracy(traits.duration, 0.5, accuracy: 0.001) + XCTAssertEqualWithAccuracy(traits.delay, 0.2, accuracy: 0.001) + XCTAssertTrue(traits.timingCurve is MDMSpringTimingCurve) + if let timingCurve = traits.timingCurve as? MDMSpringTimingCurve { + XCTAssertEqualWithAccuracy(timingCurve.mass, spring.mass, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.friction, spring.friction, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.tension, spring.tension, accuracy: 0.001) + XCTAssertEqualWithAccuracy(timingCurve.initialVelocity, spring.initialVelocity, + accuracy: 0.001) + } + XCTAssertNil(traits.repetition) + } + + func testInitializerValuesWithDurationDelayNilTimingCurveRepetition() { + let repetition = MDMRepetition(numberOfRepetitions: 5) + let traits = MDMAnimationTraits(delay: 0.2, + duration: 0.5, + timingCurve: nil, + repetition: repetition) + + XCTAssertEqualWithAccuracy(traits.duration, 0.5, accuracy: 0.001) + XCTAssertEqualWithAccuracy(traits.delay, 0.2, accuracy: 0.001) + XCTAssertNil(traits.timingCurve) + XCTAssertTrue(traits.repetition is MDMRepetition) + if let setRepetition = traits.repetition as? MDMRepetition { + XCTAssertEqualWithAccuracy(setRepetition.numberOfRepetitions, repetition.numberOfRepetitions, + accuracy: 0.001) + XCTAssertEqual(setRepetition.autoreverses, repetition.autoreverses) + } + } +} diff --git a/tests/unit/MDMModalMovementTimingTests.m b/tests/unit/MDMModalMovementTimingTests.m index f955a9a..d5aa2b2 100644 --- a/tests/unit/MDMModalMovementTimingTests.m +++ b/tests/unit/MDMModalMovementTimingTests.m @@ -18,7 +18,7 @@ #import "MotionInterchange.h" -@interface MDMModalMovementTimingTests : XCTestCase +@interface MDMAnimationTraitsSystemModalMovementTests : XCTestCase @property(nonatomic, strong) UIWindow *window; @end @@ -39,7 +39,7 @@ - (void)viewDidLayoutSubviews { @end -@implementation MDMModalMovementTimingTests +@implementation MDMAnimationTraitsSystemModalMovementTests - (void)setUp { [super setUp]; @@ -72,16 +72,19 @@ - (void)testSystemModalMovementTimingCurveMatchesModalMovementTiming { CASpringAnimation *springAnimation = (CASpringAnimation *)presentedViewController.presentationPositionAnimation; - MDMMotionTiming timing = MDMModalMovementTiming; - XCTAssertEqualWithAccuracy(timing.curve.data[MDMSpringMotionCurveDataIndexMass], - springAnimation.mass, - 0.001); - XCTAssertEqualWithAccuracy(timing.curve.data[MDMSpringMotionCurveDataIndexTension], - springAnimation.stiffness, - 0.001); - XCTAssertEqualWithAccuracy(timing.curve.data[MDMSpringMotionCurveDataIndexFriction], - springAnimation.damping, - 0.001); + MDMAnimationTraits *traits = [MDMAnimationTraits systemModalMovement]; + XCTAssertTrue([traits.timingCurve isKindOfClass:[MDMSpringTimingCurve class]], + @"Expected the system timing curve to be a %@ type, but it was '%@' instead.", + NSStringFromClass([MDMSpringTimingCurve class]), + NSStringFromClass([traits.timingCurve class])); + if ([traits.timingCurve isKindOfClass:[MDMSpringTimingCurve class]]) { + MDMSpringTimingCurve *spring = (MDMSpringTimingCurve *)traits.timingCurve; + + XCTAssertEqualWithAccuracy(spring.mass, springAnimation.mass, 0.001); + XCTAssertEqualWithAccuracy(spring.tension, springAnimation.stiffness, 0.001); + XCTAssertEqualWithAccuracy(spring.friction, springAnimation.damping, 0.001); + XCTAssertEqualWithAccuracy(spring.initialVelocity, springAnimation.initialVelocity, 0.001); + } } @end diff --git a/tests/unit/MDMMotionCurveTests.m b/tests/unit/MDMMotionCurveTests.m deleted file mode 100644 index 0cf4941..0000000 --- a/tests/unit/MDMMotionCurveTests.m +++ /dev/null @@ -1,75 +0,0 @@ -/* - Copyright 2017-present The Material Motion Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#import - -#import "MotionInterchange.h" - -@interface MDMMotionCurveTests : XCTestCase -@end - -@implementation MDMMotionCurveTests - -- (void)testLinearCurveConstantMatchesSystemLinearCurve { - MDMMotionCurve curve = MDMLinearMotionCurve; - CAMediaTimingFunction *linearTimingFunction = - [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; - MDMMotionCurve systemLinearCurve = MDMMotionCurveFromTimingFunction(linearTimingFunction); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP1X], - systemLinearCurve.data[MDMBezierMotionCurveDataIndexP1X], - 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP1Y], - systemLinearCurve.data[MDMBezierMotionCurveDataIndexP1Y], - 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP2X], - systemLinearCurve.data[MDMBezierMotionCurveDataIndexP2X], - 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP2Y], - systemLinearCurve.data[MDMBezierMotionCurveDataIndexP2Y], - 0.001); -} - -- (void)testBezierCurveData { - MDMMotionCurve curve = MDMMotionCurveMakeBezier(0.1f, 0.2f, 0.3f, 0.4f); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP1X], 0.1, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP1Y], 0.2, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP2X], 0.3, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP2Y], 0.4, 0.001); -} - -- (void)testSpringCurveData { - MDMMotionCurve curve = MDMMotionCurveMakeSpring(0.1f, 0.2f, 0.3f); - XCTAssertEqualWithAccuracy(curve.data[MDMSpringMotionCurveDataIndexMass], 0.1, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMSpringMotionCurveDataIndexTension], 0.2, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMSpringMotionCurveDataIndexFriction], 0.3, 0.001); -} - -- (void)testBezierCurveDataWithMacro { - MDMMotionCurve curve = _MDMBezier(0.1, 0.2, 0.3, 0.4); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP1X], 0.1, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP1Y], 0.2, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP2X], 0.3, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMBezierMotionCurveDataIndexP2Y], 0.4, 0.001); -} - -- (void)testSpringCurveDataWithMacro { - MDMMotionCurve curve = _MDMSpring(0.1, 0.2, 0.3); - XCTAssertEqualWithAccuracy(curve.data[MDMSpringMotionCurveDataIndexMass], 0.1, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMSpringMotionCurveDataIndexTension], 0.2, 0.001); - XCTAssertEqualWithAccuracy(curve.data[MDMSpringMotionCurveDataIndexFriction], 0.3, 0.001); -} - -@end diff --git a/tests/unit/MDMMotionCurveTests.swift b/tests/unit/MDMMotionCurveTests.swift deleted file mode 100644 index 0f1fb27..0000000 --- a/tests/unit/MDMMotionCurveTests.swift +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2017-present The Material Motion Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -import XCTest -import MotionInterchange - -class MDMMotionCurveTests: XCTestCase { - - func testBezierCurveData() { - let curve = MotionCurveMakeBezier(p1x: 0.1, p1y: 0.2, p2x: 0.3, p2y: 0.4) - XCTAssertEqualWithAccuracy(curve.data.0, 0.1, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.1, 0.2, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.2, 0.3, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.3, 0.4, accuracy: 0.001) - } - - func testBezierCurveFromTimingFunction() { - let timingFunction = CAMediaTimingFunction(controlPoints: 0.1, 0.2, 0.3, 0.4) - let curve = MotionCurve(fromTimingFunction: timingFunction) - XCTAssertEqualWithAccuracy(curve.data.0, 0.1, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.1, 0.2, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.2, 0.3, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.3, 0.4, accuracy: 0.001) - } - - func testSpringCurveData() { - let curve = MotionCurveMakeSpring(mass: 0.1, tension: 0.2, friction: 0.3) - XCTAssertEqualWithAccuracy(curve.data.0, 0.1, accuracy: 0.001) // mass - XCTAssertEqualWithAccuracy(curve.data.1, 0.2, accuracy: 0.001) // tension - XCTAssertEqualWithAccuracy(curve.data.2, 0.3, accuracy: 0.001) // friction - XCTAssertEqualWithAccuracy(curve.data.3, 0.0, accuracy: 0.001) - } - - func testReversedBezierCurve() { - let curve = MotionCurveMakeBezier(p1x: 0.1, p1y: 0.2, p2x: 0.3, p2y: 0.4) - let reversed = MotionCurveReversedBezier(fromMotionCurve: curve) - XCTAssertEqualWithAccuracy(curve.data.0, 1 - reversed.data.2, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.1, 1 - reversed.data.3, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.2, 1 - reversed.data.0, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.3, 1 - reversed.data.1, accuracy: 0.001) - } - - func testReversingBezierCurveTwiceGivesSameResult() { - let curve = MotionCurveMakeBezier(p1x: 0.1, p1y: 0.2, p2x: 0.3, p2y: 0.4) - let reversed = MotionCurveReversedBezier(fromMotionCurve: curve) - let reversedAgain = MotionCurveReversedBezier(fromMotionCurve: reversed) - XCTAssertEqualWithAccuracy(curve.data.0, reversedAgain.data.0, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.1, reversedAgain.data.1, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.2, reversedAgain.data.2, accuracy: 0.001) - XCTAssertEqualWithAccuracy(curve.data.3, reversedAgain.data.3, accuracy: 0.001) - } -} diff --git a/tests/unit/MDMRepetitionOverTimeTests.swift b/tests/unit/MDMRepetitionOverTimeTests.swift new file mode 100644 index 0000000..f571d07 --- /dev/null +++ b/tests/unit/MDMRepetitionOverTimeTests.swift @@ -0,0 +1,33 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import XCTest +import MotionInterchange + +class MDMRepetitionOverTimeTests: XCTestCase { + + func testInitializationWithDuration() { + let repetition = MDMRepetitionOverTime(duration: 5.5) + XCTAssertEqualWithAccuracy(repetition.duration, 5.5, accuracy: 0.001) + XCTAssertFalse(repetition.autoreverses) + } + + func testInitializationWithDurationAndAutoreversed() { + let repetition = MDMRepetitionOverTime(duration: 5.5, autoreverses: true) + XCTAssertEqualWithAccuracy(repetition.duration, 5.5, accuracy: 0.001) + XCTAssertTrue(repetition.autoreverses) + } +} diff --git a/tests/unit/MDMRepetitionTests.swift b/tests/unit/MDMRepetitionTests.swift new file mode 100644 index 0000000..e90f2bb --- /dev/null +++ b/tests/unit/MDMRepetitionTests.swift @@ -0,0 +1,33 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import XCTest +import MotionInterchange + +class MDMRepetitionTests: XCTestCase { + + func testInitializationWithNumberOfRepetitions() { + let repetition = MDMRepetition(numberOfRepetitions: 5.5) + XCTAssertEqualWithAccuracy(repetition.numberOfRepetitions, 5.5, accuracy: 0.001) + XCTAssertFalse(repetition.autoreverses) + } + + func testInitializationWithNumberOfRepetitionsAndAutoreversed() { + let repetition = MDMRepetition(numberOfRepetitions: 5.5, autoreverses: true) + XCTAssertEqualWithAccuracy(repetition.numberOfRepetitions, 5.5, accuracy: 0.001) + XCTAssertTrue(repetition.autoreverses) + } +} diff --git a/tests/unit/MDMSpringTimingCurve.swift b/tests/unit/MDMSpringTimingCurve.swift new file mode 100644 index 0000000..d0115c6 --- /dev/null +++ b/tests/unit/MDMSpringTimingCurve.swift @@ -0,0 +1,37 @@ +/* + Copyright 2017-present The Material Motion Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import XCTest +import MotionInterchange + +class MDMTimingCurveTests: XCTestCase { + + func testInitializerValuesWithNoInitialVelocity() { + let curve = MDMSpringTimingCurve(mass: 0.1, tension: 0.2, friction: 0.3) + XCTAssertEqualWithAccuracy(curve.mass, 0.1, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.tension, 0.2, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.friction, 0.3, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.initialVelocity, 0.0, accuracy: 0.001) + } + + func testInitializerValuesWithInitialVelocity() { + let curve = MDMSpringTimingCurve(mass: 0.1, tension: 0.2, friction: 0.3, initialVelocity: 0.4) + XCTAssertEqualWithAccuracy(curve.mass, 0.1, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.tension, 0.2, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.friction, 0.3, accuracy: 0.001) + XCTAssertEqualWithAccuracy(curve.initialVelocity, 0.4, accuracy: 0.001) + } +}