diff --git a/src/MDMMotionAnimator.m b/src/MDMMotionAnimator.m index bf49f5e..d43406c 100644 --- a/src/MDMMotionAnimator.m +++ b/src/MDMMotionAnimator.m @@ -88,27 +88,39 @@ - (void)animateWithTiming:(MDMMotionTiming)timing } animation.keyPath = keyPath; + animation.toValue = [values lastObject]; - id initialValue; - if (_beginFromCurrentState) { - if ([layer presentationLayer]) { - initialValue = [[layer presentationLayer] valueForKeyPath:keyPath]; + animation.additive = self.additive && MDMCanAnimationBeAdditive(keyPath, animation.toValue); + + // Now that we know whether the animation will be additive, we can calculate the from value. + id fromValue; + if (self.beginFromCurrentState) { + // Additive animations always read from the model layer's value so that the new displacement + // reflects the change in destination and momentum appears to be conserved across multiple + // animations. + // + // Non-additive animations should try to read from the presentation layer's current value + // because we'll be interrupting whatever animation previously existed and immediately moving + // toward the new destination. + BOOL wantsPresentationValue = !animation.additive; + + if (wantsPresentationValue && [layer presentationLayer]) { + fromValue = [[layer presentationLayer] valueForKeyPath:keyPath]; } else { - initialValue = [layer valueForKeyPath:keyPath]; + fromValue = [layer valueForKeyPath:keyPath]; } } else { - initialValue = [values firstObject]; + fromValue = [values firstObject]; } - animation.fromValue = initialValue; - animation.toValue = [values lastObject]; + animation.fromValue = fromValue; if ([animation.fromValue isEqual:animation.toValue]) { exitEarly(); return; } - MDMConfigureAnimation(animation, self.additive, timing); + MDMConfigureAnimation(animation, timing); if (timing.delay != 0) { animation.beginTime = ([layer convertTime:CACurrentMediaTime() fromLayer:nil] diff --git a/src/private/CABasicAnimation+MotionAnimator.h b/src/private/CABasicAnimation+MotionAnimator.h index 6adc7ac..5761a75 100644 --- a/src/private/CABasicAnimation+MotionAnimator.h +++ b/src/private/CABasicAnimation+MotionAnimator.h @@ -24,13 +24,15 @@ FOUNDATION_EXPORT CABasicAnimation *MDMAnimationFromTiming(MDMMotionTiming timing, CGFloat timeScaleFactor); -// 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. +// Returns a Boolean indicating whether or not an animation with the given key path and toValue +// can be animated additively. +FOUNDATION_EXPORT BOOL MDMCanAnimationBeAdditive(NSString *keyPath, id toValue); + +// If the animation's additive property is enabled, then its from/to values will be transformed into +// additive equivalents. // // 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); +FOUNDATION_EXPORT void MDMConfigureAnimation(CABasicAnimation *animation, MDMMotionTiming timing); diff --git a/src/private/CABasicAnimation+MotionAnimator.m b/src/private/CABasicAnimation+MotionAnimator.m index ff555e7..56f5602 100644 --- a/src/private/CABasicAnimation+MotionAnimator.m +++ b/src/private/CABasicAnimation+MotionAnimator.m @@ -86,10 +86,12 @@ static BOOL IsCGSizeType(id someValue) { return animation; } -void MDMConfigureAnimation(CABasicAnimation *animation, - BOOL wantsAdditive, - MDMMotionTiming timing) { - if (!wantsAdditive && timing.curve.type != MDMMotionCurveTypeSpring) { +BOOL MDMCanAnimationBeAdditive(NSString *keyPath, id toValue) { + return IsNumberValue(toValue) || IsCGSizeType(toValue) || IsCGPointType(toValue); +} + +void MDMConfigureAnimation(CABasicAnimation *animation, MDMMotionTiming timing) { + if (!animation.additive && timing.curve.type != MDMMotionCurveTypeSpring) { return; // Nothing to do here. } @@ -126,10 +128,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation, CGFloat displacement = to - from; CGFloat additiveDisplacement = -displacement; - if (wantsAdditive) { + if (animation.additive) { animation.fromValue = @(additiveDisplacement); animation.toValue = @0; - animation.additive = true; } #pragma clang diagnostic push @@ -187,10 +188,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation, CGSize to = [animation.toValue CGSizeValue]; CGSize additiveDisplacement = CGSizeMake(from.width - to.width, from.height - to.height); - if (wantsAdditive) { + if (animation.additive) { animation.fromValue = [NSValue valueWithCGSize:additiveDisplacement]; animation.toValue = [NSValue valueWithCGSize:CGSizeZero]; - animation.additive = true; } #pragma clang diagnostic push @@ -219,10 +219,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation, CGPoint to = [animation.toValue CGPointValue]; CGPoint additiveDisplacement = CGPointMake(from.x - to.x, from.y - to.y); - if (wantsAdditive) { + if (animation.additive) { animation.fromValue = [NSValue valueWithCGPoint:additiveDisplacement]; animation.toValue = [NSValue valueWithCGPoint:CGPointZero]; - animation.additive = true; } #pragma clang diagnostic push