From 32c78d4d93b6c82ff55693c54176b2826f718e5e Mon Sep 17 00:00:00 2001 From: featherless Date: Thu, 14 Dec 2017 15:48:57 -0500 Subject: [PATCH] Add support for additively animating bounds. (#93) Closes https://github.com/material-motion/motion-animator-objc/issues/74 --- src/MDMAnimatableKeyPaths.h | 4 +- src/private/CABasicAnimation+MotionAnimator.m | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/MDMAnimatableKeyPaths.h b/src/MDMAnimatableKeyPaths.h index 2a9bbc3..e02a037 100644 --- a/src/MDMAnimatableKeyPaths.h +++ b/src/MDMAnimatableKeyPaths.h @@ -58,9 +58,7 @@ FOUNDATION_EXPORT MDMAnimatableKeyPath MDMKeyPathBackgroundColor NS_SWIFT_NAME(b Equivalent UIView property: bounds Equivalent CALayer property: bounds Expected value type: CGRect or NSValue (containing a CGRect). - Additive animation supported: No. - TODO( https://github.com/material-motion/motion-animator-objc/issues/74 ): - Add support for additively animating CGRect types. + Additive animation supported: Yes. */ FOUNDATION_EXPORT MDMAnimatableKeyPath MDMKeyPathBounds NS_SWIFT_NAME(bounds); diff --git a/src/private/CABasicAnimation+MotionAnimator.m b/src/private/CABasicAnimation+MotionAnimator.m index 087f97f..3f212b5 100644 --- a/src/private/CABasicAnimation+MotionAnimator.m +++ b/src/private/CABasicAnimation+MotionAnimator.m @@ -44,6 +44,15 @@ static BOOL IsCGSizeType(id someValue) { return NO; } +static BOOL IsCGRectType(id someValue) { + if ([someValue isKindOfClass:[NSValue class]]) { + NSValue *asValue = (NSValue *)someValue; + const char *objCType = @encode(CGRect); + return strncmp(asValue.objCType, objCType, strlen(objCType)) == 0; + } + return NO; +} + static BOOL IsCATransform3DType(id someValue) { if ([someValue isKindOfClass:[NSValue class]]) { NSValue *asValue = (NSValue *)someValue; @@ -270,6 +279,39 @@ void MDMConfigureAnimation(CABasicAnimation *animation, MDMAnimationTraits * tra } } + } else if (IsCGRectType(animation.toValue)) { + CGRect from = [animation.fromValue CGRectValue]; + CGRect to = [animation.toValue CGRectValue]; + CGRect additiveDisplacement = CGRectMake(from.origin.x - to.origin.x, + from.origin.y - to.origin.y, + from.size.width - to.size.width, + from.size.height - to.size.height); + + if (animation.additive) { + animation.fromValue = [NSValue valueWithCGRect:additiveDisplacement]; + animation.toValue = [NSValue valueWithCGRect:CGRectZero]; + } + + if (isSpringAnimation) { + // Core Animation's velocity system is single dimensional, so we pick the dominant direction + // of movement and normalize accordingly. + CGFloat biggestDelta = additiveDisplacement.origin.x; + if (fabs(additiveDisplacement.origin.y) > fabs(biggestDelta)) { + biggestDelta = additiveDisplacement.origin.y; + } + if (fabs(additiveDisplacement.size.width) > fabs(biggestDelta)) { + biggestDelta = additiveDisplacement.size.width; + } + if (fabs(additiveDisplacement.size.height) > fabs(biggestDelta)) { + biggestDelta = additiveDisplacement.size.height; + } + CGFloat displacement = -biggestDelta; + CGFloat absoluteInitialVelocity = springTimingCurve.initialVelocity; + if (fabs(displacement) > 0.00001) { + springAnimation.initialVelocity = absoluteInitialVelocity / displacement; + } + } + } else if (IsCATransform3DType(animation.toValue)) { CATransform3D from = [animation.fromValue CATransform3DValue]; CATransform3D to = [animation.toValue CATransform3DValue];