Skip to content

Commit

Permalink
Ensure DecayAnimation Completion Block is Called Only Once. Fixes #1
Browse files Browse the repository at this point in the history
  • Loading branch information
b3ll committed Jan 6, 2021
1 parent 4112265 commit 68b5cd6
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 9 deletions.
4 changes: 2 additions & 2 deletions Sources/Motion/Animations/BasicAnimation.swift
Expand Up @@ -129,8 +129,8 @@ public final class BasicAnimation<Value: SIMDRepresentable>: ValueAnimation<Valu
Stops the animation and optionally resolves it immediately (jumping to the `toValue`).
- Parameters:
- resolveImmediately: Whether or not the animation should jump to the `toValue` without animation. Defaults to `false`.
- postValueChanged: If `true` is supplied for `resolveImmediately`, this controls whether not `valueChanged` upon changing `value` to toValue`.
- resolveImmediately: Whether or not the animation should jump to the `toValue` without animation and invoke the completion. Defaults to `false`.
- postValueChanged: If `true` is supplied for `resolveImmediately`, this controls whether not `valueChanged` is called upon changing `value` to `toValue`.
*/
public override func stop(resolveImmediately: Bool = false, postValueChanged: Bool = false) {
super.stop(resolveImmediately: resolveImmediately, postValueChanged: postValueChanged)
Expand Down
10 changes: 7 additions & 3 deletions Sources/Motion/Animations/DecayAnimation.swift
Expand Up @@ -82,16 +82,20 @@ public final class DecayAnimation<Value: SIMDRepresentable>: ValueAnimation<Valu
Stops the animation and optionally resolves it immediately.
- Parameters:
- resolveImmediately: Whether or not the animation should jump to its projected value without animation. Defaults to `false`.
- postValueChanged: If `true` is supplied for `resolveImmediately`, this controls whether not `valueChanged` upon changing `value` to the end value.
- resolveImmediately: Whether or not the animation should jump to zero `velocity` and invoke the completion. Defaults to `false`.
- postValueChanged: If `true` is supplied for `resolveImmediately`, this controls whether not `valueChanged` is called upon changing `value` to the end value.
- Note: `resolveImmediately` and `postValueChanged` currently are ignored.
They will be implemented at a later date when the logic for projecting decaying functions is worked out.
*/
public override func stop(resolveImmediately: Bool = false, postValueChanged: Bool = false) {
// We don't call super here, as jumping to the end requires knowing the end point, and we don't know that (yet).
self.enabled = false
completion?()
self.velocity = .zero

if resolveImmediately {
completion?()
}
}

// MARK: - Disabled API
Expand Down
4 changes: 2 additions & 2 deletions Sources/Motion/Animations/SpringAnimation.swift
Expand Up @@ -246,8 +246,8 @@ public final class SpringAnimation<Value: SIMDRepresentable>: ValueAnimation<Val
Stops the animation and optionally resolves it immediately (jumping to the `toValue`).
- Parameters:
- resolveImmediately: Whether or not the animation should jump to the `toValue` without animation. Defaults to `false`.
- postValueChanged: If `true` is supplied for `resolveImmediately`, this controls whether not `valueChanged` upon changing `value` to toValue`.
- resolveImmediately: Whether or not the animation should jump to the `toValue` without animation and invoke the completion. Defaults to `false`.
- postValueChanged: If `true` is supplied for `resolveImmediately`, this controls whether not `valueChanged` is called upon changing `value` to `toValue`.
*/
public override func stop(resolveImmediately: Bool = false, postValueChanged: Bool = false) {
super.stop(resolveImmediately: resolveImmediately, postValueChanged: postValueChanged)
Expand Down
3 changes: 2 additions & 1 deletion Tests/MotionTests/BasicAnimationTests.swift
Expand Up @@ -76,7 +76,8 @@ final class BasicAnimationTests: XCTestCase {
expectCompletionCalled.fulfill()
}

tickAnimationUntilResolved(basicAnimation)
tickAnimationForDuration(basicAnimation, maxDuration: 0.1)
basicAnimation.stop(resolveImmediately: true, postValueChanged: true)

wait(for: [expectValueChangedCalled, expectCompletionCalled], timeout: 0.0)
}
Expand Down
20 changes: 20 additions & 0 deletions Tests/MotionTests/DecayAnimationTests.swift
Expand Up @@ -58,6 +58,26 @@ final class DecayAnimationTests: XCTestCase {
wait(for: [expectCompletionCalled, expectDecayVelocityZero], timeout: 0.0)
}

func testDecayResolveImmediately() {
let decay = DecayAnimation<CGFloat>()

let expectCompletionCalled = XCTestExpectation(description: "Decay animated from \(decay.value) to ")
let expectDecayVelocityZero = XCTestExpectation(description: "Decay animated from \(decay.value) to ")
decay.completion = { [unowned decay] in
expectCompletionCalled.fulfill()

if decay.velocity <= 0.5 {
expectDecayVelocityZero.fulfill()
}
}
decay.velocity = 2000.0

tickAnimationForDuration(decay, maxDuration: 0.1)
decay.stop(resolveImmediately: true, postValueChanged: true)

wait(for: [expectCompletionCalled, expectDecayVelocityZero], timeout: 0.0)
}

// MARK: - CAKeyframeAnimationEmittable Tests

func testCreateCAKeyframeAnimationFromDecayAnimation() {
Expand Down
3 changes: 2 additions & 1 deletion Tests/MotionTests/SpringAnimationTests.swift
Expand Up @@ -149,7 +149,8 @@ final class SpringAnimationTests: XCTestCase {
expectCompletionCalled.fulfill()
}

tickAnimationUntilResolved(spring)
tickAnimationForDuration(spring, maxDuration: 0.1)
spring.stop(resolveImmediately: true, postValueChanged: true)

wait(for: [expectValueChangedCalled, expectCompletionCalled], timeout: 0.0)
}
Expand Down

0 comments on commit 68b5cd6

Please sign in to comment.