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

Commit 433eb5f

Browse files
authored
Add fallback mechanism for non-additive animations when beginFromCurrentState is enabled. (#76)
When beginFromCurrentState is enabled, the user expects any new animations to animate from the layer's current rendered state. What this actually means in practice depends on whether the new animation is additive or not. If the animation is additive, we always want to read from the model layer because we need to calculate displacement from the old destination to the new destination. If the animation is not additive, we read from the presentation layer if available so that the view begins animating from its current rendered position. The difference between the two approaches is that additive animations will always appear to preserve momentum - potentially even moving in the opposite direction for a brief moment - while non-additive animations will instantly begin moving toward the new destination.
1 parent efa8937 commit 433eb5f

File tree

3 files changed

+37
-24
lines changed

3 files changed

+37
-24
lines changed

src/MDMMotionAnimator.m

+21-9
Original file line numberDiff line numberDiff line change
@@ -88,27 +88,39 @@ - (void)animateWithTiming:(MDMMotionTiming)timing
8888
}
8989

9090
animation.keyPath = keyPath;
91+
animation.toValue = [values lastObject];
9192

92-
id initialValue;
93-
if (_beginFromCurrentState) {
94-
if ([layer presentationLayer]) {
95-
initialValue = [[layer presentationLayer] valueForKeyPath:keyPath];
93+
animation.additive = self.additive && MDMCanAnimationBeAdditive(keyPath, animation.toValue);
94+
95+
// Now that we know whether the animation will be additive, we can calculate the from value.
96+
id fromValue;
97+
if (self.beginFromCurrentState) {
98+
// Additive animations always read from the model layer's value so that the new displacement
99+
// reflects the change in destination and momentum appears to be conserved across multiple
100+
// animations.
101+
//
102+
// Non-additive animations should try to read from the presentation layer's current value
103+
// because we'll be interrupting whatever animation previously existed and immediately moving
104+
// toward the new destination.
105+
BOOL wantsPresentationValue = !animation.additive;
106+
107+
if (wantsPresentationValue && [layer presentationLayer]) {
108+
fromValue = [[layer presentationLayer] valueForKeyPath:keyPath];
96109
} else {
97-
initialValue = [layer valueForKeyPath:keyPath];
110+
fromValue = [layer valueForKeyPath:keyPath];
98111
}
99112
} else {
100-
initialValue = [values firstObject];
113+
fromValue = [values firstObject];
101114
}
102115

103-
animation.fromValue = initialValue;
104-
animation.toValue = [values lastObject];
116+
animation.fromValue = fromValue;
105117

106118
if ([animation.fromValue isEqual:animation.toValue]) {
107119
exitEarly();
108120
return;
109121
}
110122

111-
MDMConfigureAnimation(animation, self.additive, timing);
123+
MDMConfigureAnimation(animation, timing);
112124

113125
if (timing.delay != 0) {
114126
animation.beginTime = ([layer convertTime:CACurrentMediaTime() fromLayer:nil]

src/private/CABasicAnimation+MotionAnimator.h

+7-5
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424
FOUNDATION_EXPORT
2525
CABasicAnimation *MDMAnimationFromTiming(MDMMotionTiming timing, CGFloat timeScaleFactor);
2626

27-
// Attempts to configure the provided animation to be additive and, if the animation is a spring
28-
// animation, will extract the initial velocity from the timing and apply it to the animation.
27+
// Returns a Boolean indicating whether or not an animation with the given key path and toValue
28+
// can be animated additively.
29+
FOUNDATION_EXPORT BOOL MDMCanAnimationBeAdditive(NSString *keyPath, id toValue);
30+
31+
// If the animation's additive property is enabled, then its from/to values will be transformed into
32+
// additive equivalents.
2933
//
3034
// Not all animation value types support being additive. If an animation's value type was not
3135
// supported, the animation's values will not be modified.
3236
//
3337
// If the from and to values of the animation match then the behavior is undefined.
34-
FOUNDATION_EXPORT void MDMConfigureAnimation(CABasicAnimation *animation,
35-
BOOL wantsAdditive,
36-
MDMMotionTiming timing);
38+
FOUNDATION_EXPORT void MDMConfigureAnimation(CABasicAnimation *animation, MDMMotionTiming timing);

src/private/CABasicAnimation+MotionAnimator.m

+9-10
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,12 @@ static BOOL IsCGSizeType(id someValue) {
8686
return animation;
8787
}
8888

89-
void MDMConfigureAnimation(CABasicAnimation *animation,
90-
BOOL wantsAdditive,
91-
MDMMotionTiming timing) {
92-
if (!wantsAdditive && timing.curve.type != MDMMotionCurveTypeSpring) {
89+
BOOL MDMCanAnimationBeAdditive(NSString *keyPath, id toValue) {
90+
return IsNumberValue(toValue) || IsCGSizeType(toValue) || IsCGPointType(toValue);
91+
}
92+
93+
void MDMConfigureAnimation(CABasicAnimation *animation, MDMMotionTiming timing) {
94+
if (!animation.additive && timing.curve.type != MDMMotionCurveTypeSpring) {
9395
return; // Nothing to do here.
9496
}
9597

@@ -126,10 +128,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation,
126128
CGFloat displacement = to - from;
127129
CGFloat additiveDisplacement = -displacement;
128130

129-
if (wantsAdditive) {
131+
if (animation.additive) {
130132
animation.fromValue = @(additiveDisplacement);
131133
animation.toValue = @0;
132-
animation.additive = true;
133134
}
134135

135136
#pragma clang diagnostic push
@@ -187,10 +188,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation,
187188
CGSize to = [animation.toValue CGSizeValue];
188189
CGSize additiveDisplacement = CGSizeMake(from.width - to.width, from.height - to.height);
189190

190-
if (wantsAdditive) {
191+
if (animation.additive) {
191192
animation.fromValue = [NSValue valueWithCGSize:additiveDisplacement];
192193
animation.toValue = [NSValue valueWithCGSize:CGSizeZero];
193-
animation.additive = true;
194194
}
195195

196196
#pragma clang diagnostic push
@@ -219,10 +219,9 @@ void MDMConfigureAnimation(CABasicAnimation *animation,
219219
CGPoint to = [animation.toValue CGPointValue];
220220
CGPoint additiveDisplacement = CGPointMake(from.x - to.x, from.y - to.y);
221221

222-
if (wantsAdditive) {
222+
if (animation.additive) {
223223
animation.fromValue = [NSValue valueWithCGPoint:additiveDisplacement];
224224
animation.toValue = [NSValue valueWithCGPoint:CGPointZero];
225-
animation.additive = true;
226225
}
227226

228227
#pragma clang diagnostic push

0 commit comments

Comments
 (0)