Skip to content
This repository has been archived by the owner on Nov 21, 2017. It is now read-only.

Commit

Permalink
Implement Tween plan + unit test
Browse files Browse the repository at this point in the history
Summary:
Spec: https://material-motion.gitbooks.io/material-motion-starmap/content/specifications/motion_family/tween.html

Closes #7

Reviewers: O4 Material Motion Apple platform reviewers, O2 Material Motion, markwei

Reviewed By: O4 Material Motion Apple platform reviewers, O2 Material Motion, markwei

Subscribers: markwei

Tags: #material_motion

Differential Revision: http://codereview.cc/D1683
  • Loading branch information
Jeff Verkoeyen committed Oct 6, 2016
1 parent be727d7 commit 0f2a038
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 0 deletions.
8 changes: 8 additions & 0 deletions examples/apps/Catalog/Catalog.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
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 */; };
66A277841DA2E9DD00AFA57B /* TweenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66A277831DA2E9DD00AFA57B /* TweenTests.swift */; };
66A2778D1DA6CDE800AFA57B /* ObjectiveCAPITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A2778C1DA6CDE800AFA57B /* ObjectiveCAPITests.m */; };
66E6202E1D46B4250021B138 /* CoreAnimationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E6202D1D46B4250021B138 /* CoreAnimationTests.swift */; };
66E620301D47D2340021B138 /* SchedulerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E6202F1D47D2340021B138 /* SchedulerDelegate.swift */; };
66FB71CE1FA2B5DC89A7883C /* Pods_MaterialMotionCoreAnimationFamily_UnitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 443B77A9B48B8E8395819E7E /* Pods_MaterialMotionCoreAnimationFamily_UnitTests.framework */; };
Expand Down Expand Up @@ -38,6 +40,8 @@
666FAA941D384A6B000363DA /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
666FAA9A1D384A6B000363DA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../UnitTests/Info.plist; sourceTree = "<group>"; };
666FAAA71D384D51000363DA /* UnitTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "UnitTests-Bridging-Header.h"; path = "../UnitTests/UnitTests-Bridging-Header.h"; sourceTree = "<group>"; };
66A277831DA2E9DD00AFA57B /* TweenTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweenTests.swift; sourceTree = "<group>"; };
66A2778C1DA6CDE800AFA57B /* ObjectiveCAPITests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ObjectiveCAPITests.m; sourceTree = "<group>"; };
66E6202D1D46B4250021B138 /* CoreAnimationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreAnimationTests.swift; sourceTree = "<group>"; };
66E6202F1D47D2340021B138 /* SchedulerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerDelegate.swift; sourceTree = "<group>"; };
B2AEAB79F993F15CB0185C24 /* Pods-MaterialMotionCoreAnimationFamily-UnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MaterialMotionCoreAnimationFamily-UnitTests.debug.xcconfig"; path = "../../../Pods/Target Support Files/Pods-MaterialMotionCoreAnimationFamily-UnitTests/Pods-MaterialMotionCoreAnimationFamily-UnitTests.debug.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -115,6 +119,8 @@
children = (
66E6202D1D46B4250021B138 /* CoreAnimationTests.swift */,
66E6202F1D47D2340021B138 /* SchedulerDelegate.swift */,
66A277831DA2E9DD00AFA57B /* TweenTests.swift */,
66A2778C1DA6CDE800AFA57B /* ObjectiveCAPITests.m */,
);
name = tests;
path = ../../../tests/unit;
Expand Down Expand Up @@ -376,6 +382,8 @@
buildActionMask = 2147483647;
files = (
66E620301D47D2340021B138 /* SchedulerDelegate.swift in Sources */,
66A277841DA2E9DD00AFA57B /* TweenTests.swift in Sources */,
66A2778D1DA6CDE800AFA57B /* ObjectiveCAPITests.m in Sources */,
66E6202E1D46B4250021B138 /* CoreAnimationTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
64 changes: 64 additions & 0 deletions src/Tween.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2016-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 MaterialMotionRuntime

/** Interpolate a CALayer property from one value to another. */
@objc(MDMTween)
public final class Tween: NSObject, Plan {
/** The key path of the property whose value will be tweened. */
public var keyPath: String

/** The duration of the animation in seconds. */
public var duration: CFTimeInterval

/** The delay of the animation in seconds. */
public var delay: CFTimeInterval = 0

/** The initial value of the tween. See CABasicAnimation documentation for more details. */
public var from: Any?

/** The final value of the tween. See CABasicAnimation documentation for more details. */
public var to: Any?

/**
The timing function to apply to the animation.

A nil timing function indicates linear pacing.
*/
public var timingFunction: CAMediaTimingFunction?

@objc(initWithKeyPath:duration:)
public init(_ keyPath: String, duration: CFTimeInterval) {
self.keyPath = keyPath
self.duration = duration
super.init()
}

/** The performer that will fulfill this plan. */
public func performerClass() -> AnyClass {
return TweenPerformer.self
}
/** Returns a copy of this plan. */
public func copy(with zone: NSZone? = nil) -> Any {
let tween = Tween(keyPath, duration: duration)
tween.from = from
tween.to = to
tween.timingFunction = timingFunction
tween.delay = delay
return tween
}
}
49 changes: 49 additions & 0 deletions src/private/TweenPerformer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
Copyright 2016-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 UIKit
import MaterialMotionRuntime

class TweenPerformer: NSObject, PlanPerforming, ComposablePerforming {
let target: CALayer
required init(target: Any) {
if let view = target as? UIView {
self.target = view.layer
} else {
self.target = target as! CALayer
}
}

func add(plan: Plan) {
let tween = plan as! Tween

let animation = CABasicAnimation(keyPath: tween.keyPath)
animation.fromValue = tween.from
animation.toValue = tween.to
animation.timingFunction = tween.timingFunction
animation.duration = tween.duration
animation.beginTime = tween.delay

let transaction = Transaction()
transaction.add(plan: animation, to: target)
emitter.emit(transaction: transaction)
}

var emitter: TransactionEmitting!
func set(transactionEmitter: TransactionEmitting) {
emitter = transactionEmitter
}
}
36 changes: 36 additions & 0 deletions tests/unit/ObjectiveCAPITests.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright 2016-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/XCTest.h>

@import MaterialMotionCoreAnimationFamily;

@interface ObjectiveCAPITests : XCTestCase
@end

@implementation ObjectiveCAPITests

- (void)testTweenAPI {
MDMTween *tween = [[MDMTween alloc] initWithKeyPath:@"opacity" duration:0.1];
tween.delay = 0.1;
tween.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
tween.from = @2;
tween.to = @2;
XCTAssertEqual(tween.keyPath, @"opacity");
XCTAssertEqual(tween.duration, 0.1);
}

@end
41 changes: 41 additions & 0 deletions tests/unit/TweenTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
Copyright 2016-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 MaterialMotionRuntime
import MaterialMotionCoreAnimationFamily

class TweenTests: XCTestCase {

func testDidPerformAndIdle() {
let animation = Tween("opacity", duration: 0.1)

let view = UIView()

let scheduler = Scheduler()
let delegate = TestableSchedulerDelegate()
delegate.didIdleExpectation = expectation(description: "Did idle")
scheduler.delegate = delegate

let transaction = Transaction()
transaction.add(plan: animation, to: view)
scheduler.commit(transaction: transaction)

waitForExpectations(timeout: 0.3)

XCTAssertEqual(scheduler.activityState, .idle)
}
}

0 comments on commit 0f2a038

Please sign in to comment.