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

Commit edabddc

Browse files
authored
Fix bug where CGPoint and CGSize animations were not extracting initialVelocity (#39)
* Fix bug where CGPoint and CGSize animations were not extracting initialVelocity. Added unit tests to verify more property types. * Better test results.
1 parent cef4416 commit edabddc

File tree

2 files changed

+78
-53
lines changed

2 files changed

+78
-53
lines changed

src/private/CABasicAnimation+MotionAnimator.m

+6-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation,
191191
biggestDelta = additiveDisplacement.height;
192192
}
193193
CGFloat displacement = -biggestDelta;
194-
springAnimation.initialVelocity = springAnimation.initialVelocity / displacement;
194+
CGFloat absoluteInitialVelocity =
195+
timing.curve.data[MDMSpringMotionCurveDataIndexInitialVelocity];
196+
springAnimation.initialVelocity = absoluteInitialVelocity / displacement;
195197
}
196198

197199
} else if ([positionKeyPaths containsObject:animation.keyPath]) {
@@ -218,7 +220,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation,
218220
biggestDelta = additiveDisplacement.y;
219221
}
220222
CGFloat displacement = -biggestDelta;
221-
springAnimation.initialVelocity = springAnimation.initialVelocity / displacement;
223+
CGFloat absoluteInitialVelocity =
224+
timing.curve.data[MDMSpringMotionCurveDataIndexInitialVelocity];
225+
springAnimation.initialVelocity = absoluteInitialVelocity / displacement;
222226
}
223227
}
224228
}

tests/unit/InitialVelocityTests.swift

+72-51
Original file line numberDiff line numberDiff line change
@@ -44,75 +44,96 @@ class InitialVelocityTests: XCTestCase {
4444
}
4545

4646
func testVelocityAmplitudeMatchesDisplacementWithPositiveDisplacement() {
47-
let timing = MotionTiming(delay: 0,
48-
duration: 0.7,
49-
curve: .init(type: .spring, data: (1, 1, 1, 50)),
50-
repetition: .init(type: .none, amount: 0, autoreverses: false))
51-
animator.animate(with: timing, to: CALayer(), withValues: [0, 100], keyPath: .opacity)
52-
53-
XCTAssertEqual(addedAnimations.count, 1)
54-
let animation = addedAnimations.first as! CASpringAnimation
55-
XCTAssertEqual(animation.initialVelocity, 0.5)
47+
let velocity: CGFloat = 50
48+
animate(from: 0, to: 100, withVelocity: velocity)
49+
50+
XCTAssertEqual(addedAnimations.count, 3)
51+
addedAnimations.flatMap { $0 as? CASpringAnimation }.forEach { animation in
52+
XCTAssertEqual(animation.initialVelocity, 0.5,
53+
"from: \(animation.fromValue!), "
54+
+ "to: \(animation.toValue!), "
55+
+ "withVelocity: \(velocity)")
56+
}
5657
}
5758

5859
func testVelocityAmplitudeMatchesDisplacementWithNegativeDisplacement() {
59-
let timing = MotionTiming(delay: 0,
60-
duration: 0.7,
61-
curve: .init(type: .spring, data: (1, 1, 1, -50)),
62-
repetition: .init(type: .none, amount: 0, autoreverses: false))
63-
animator.animate(with: timing, to: CALayer(), withValues: [100, 0], keyPath: .opacity)
64-
65-
XCTAssertEqual(addedAnimations.count, 1)
66-
let animation = addedAnimations.first as! CASpringAnimation
67-
XCTAssertEqual(animation.initialVelocity, 0.5)
60+
let velocity: CGFloat = -50
61+
animate(from: 100, to: 0, withVelocity: velocity)
62+
63+
XCTAssertEqual(addedAnimations.count, 3)
64+
addedAnimations.flatMap { $0 as? CASpringAnimation }.forEach { animation in
65+
XCTAssertEqual(animation.initialVelocity, 0.5,
66+
"from: \(animation.fromValue!), "
67+
+ "to: \(animation.toValue!), "
68+
+ "withVelocity: \(velocity)")
69+
}
6870
}
6971

7072
func testVelocityTowardsDestinationIsPositiveWithPositiveDisplacement() {
71-
let timing = MotionTiming(delay: 0,
72-
duration: 0.7,
73-
curve: .init(type: .spring, data: (1, 1, 1, 100)),
74-
repetition: .init(type: .none, amount: 0, autoreverses: false))
75-
animator.animate(with: timing, to: CALayer(), withValues: [0, 100], keyPath: .opacity)
76-
77-
XCTAssertEqual(addedAnimations.count, 1)
78-
let animation = addedAnimations.first as! CASpringAnimation
79-
XCTAssertGreaterThan(animation.initialVelocity, 0)
73+
let velocity: CGFloat = 100
74+
animate(from: 0, to: 100, withVelocity: velocity)
75+
76+
XCTAssertEqual(addedAnimations.count, 3)
77+
addedAnimations.flatMap { $0 as? CASpringAnimation }.forEach { animation in
78+
XCTAssertGreaterThan(animation.initialVelocity, 0,
79+
"from: \(animation.fromValue!), "
80+
+ "to: \(animation.toValue!), "
81+
+ "withVelocity: \(velocity)")
82+
}
8083
}
8184

8285
func testVelocityAwayFromDestinationIsNegativeWithPositiveDisplacement() {
83-
let timing = MotionTiming(delay: 0,
84-
duration: 0.7,
85-
curve: .init(type: .spring, data: (1, 1, 1, -100)),
86-
repetition: .init(type: .none, amount: 0, autoreverses: false))
87-
animator.animate(with: timing, to: CALayer(), withValues: [0, 100], keyPath: .opacity)
88-
89-
XCTAssertEqual(addedAnimations.count, 1)
90-
let animation = addedAnimations.first as! CASpringAnimation
91-
XCTAssertLessThan(animation.initialVelocity, 0)
86+
let velocity: CGFloat = -100
87+
animate(from: 0, to: 100, withVelocity: velocity)
88+
89+
XCTAssertEqual(addedAnimations.count, 3)
90+
addedAnimations.flatMap { $0 as? CASpringAnimation }.forEach { animation in
91+
XCTAssertLessThan(animation.initialVelocity, 0,
92+
"from: \(animation.fromValue!), "
93+
+ "to: \(animation.toValue!), "
94+
+ "withVelocity: \(velocity)")
95+
}
9296
}
9397

9498
func testVelocityTowardsDestinationIsPositiveWithNegativeDisplacement() {
95-
let timing = MotionTiming(delay: 0,
96-
duration: 0.7,
97-
curve: .init(type: .spring, data: (1, 1, 1, -100)),
98-
repetition: .init(type: .none, amount: 0, autoreverses: false))
99-
animator.animate(with: timing, to: CALayer(), withValues: [100, 0], keyPath: .opacity)
100-
101-
XCTAssertEqual(addedAnimations.count, 1)
102-
let animation = addedAnimations.first as! CASpringAnimation
103-
XCTAssertGreaterThan(animation.initialVelocity, 0)
99+
let velocity: CGFloat = -100
100+
animate(from: 100, to: 0, withVelocity: velocity)
101+
102+
XCTAssertEqual(addedAnimations.count, 3)
103+
addedAnimations.flatMap { $0 as? CASpringAnimation }.forEach { animation in
104+
XCTAssertGreaterThan(animation.initialVelocity, 0,
105+
"from: \(animation.fromValue!), "
106+
+ "to: \(animation.toValue!), "
107+
+ "withVelocity: \(velocity)")
108+
}
104109
}
105110

106111
func testVelocityAwayFromDestinationIsNegativeWithNegativeDisplacement() {
112+
let velocity: CGFloat = 100
113+
animate(from: 100, to: 0, withVelocity: velocity)
114+
115+
XCTAssertEqual(addedAnimations.count, 3)
116+
addedAnimations.flatMap { $0 as? CASpringAnimation }.forEach { animation in
117+
XCTAssertLessThan(animation.initialVelocity, 0,
118+
"from: \(animation.fromValue!), "
119+
+ "to: \(animation.toValue!), "
120+
+ "withVelocity: \(velocity)")
121+
}
122+
}
123+
124+
private func animate(from: CGFloat, to: CGFloat, withVelocity velocity: CGFloat) {
107125
let timing = MotionTiming(delay: 0,
108126
duration: 0.7,
109-
curve: .init(type: .spring, data: (1, 1, 1, 100)),
127+
curve: .init(type: .spring, data: (1, 1, 1, velocity)),
110128
repetition: .init(type: .none, amount: 0, autoreverses: false))
111-
animator.animate(with: timing, to: CALayer(), withValues: [100, 0], keyPath: .opacity)
112-
113-
XCTAssertEqual(addedAnimations.count, 1)
114-
let animation = addedAnimations.first as! CASpringAnimation
115-
XCTAssertLessThan(animation.initialVelocity, 0)
129+
animator.animate(with: timing, to: CALayer(), withValues: [from, to],
130+
keyPath: .opacity)
131+
animator.animate(with: timing, to: CALayer(), withValues: [CGPoint(x: from, y: from),
132+
CGPoint(x: to, y: to)],
133+
keyPath: .position)
134+
animator.animate(with: timing, to: CALayer(), withValues: [CGSize(width: from, height: from),
135+
CGSize(width: to, height: to)],
136+
keyPath: .init(rawValue: "bounds.size"))
116137
}
117138
}
118139

0 commit comments

Comments
 (0)