4

I am animating a UIView using its CALayer's affineTransform property. The transform I am animating to is built this way : CGAffineTransformTranslate(CGAffineTransformScale(zoomContainer.layer.transform, scaleX, scaleY), translateX, translateY).

Now I want to improve this animation and make it interactive. Thus I need to interpolate the CGAffineTransform using a completion percentage. However, I can't seem to find the way the system interpolates the transformation when animating it for me. I always end up with weird curves where the translation isn't synchronized with the scaling. Here's the code I currently have :

CGFloat completion = fmax(0, fmin(completionPercentage, 1));
CGFloat scaleX = CGRectGetWidth(zoomContainerInitialBounds) / CGRectGetWidth(zoomTargetInitialFrame);
CGFloat scaleY = CGRectGetHeight(zoomContainerInitialBounds) / CGRectGetHeight(zoomTargetInitialFrame);
scaleX = 1 + ((scaleX - 1) * (1 - completion));
scaleY = 1 + ((scaleY - 1) * (1 - completion));
CGFloat translateX = CGRectGetMidX(zoomContainerInitialBounds) - CGRectGetMidX(zoomTargetInitialFrame);
CGFloat translateY = CGRectGetMidY(zoomContainerInitialBounds) - CGRectGetMidY(zoomTargetInitialFrame);
translateX *= (1 - completion);
translateY *= (1 - completion);
zoomContainer.layer.affineTransform = CGAffineTransformTranslate(CGAffineTransformScale(initialTransform, scaleX, scaleY), translateX, translateY);

What do I need to change to interpolate the transform the same way UIKit does it for me?

Dalzhim
  • 1,970
  • 1
  • 17
  • 34
  • Working on this problem, I have diminished the "boomerang" curve I am having by calculating the scale factor with `pow(2, log2f(scaleX) * (1 - completion))` instead of `1 + ((scaleX - 1) * (1 - completion))`. But there is still an undesirable curve. – Dalzhim Nov 06 '13 at 19:46

1 Answers1

3

If you want to be able to interact with the animation (maybe using a gesture or a slider or some other mechanism) then you can use a little trick where you "pause" the animation by setting the speed of the view's layer to 0 and then move the animation to a specific point in time by setting the timeOffset of the layer.

I have an explanation of this in an answer to a similar (but animating a different property) question and I a more detailed explanation in the context of animation timing in a blog post.


So that this isn't just a link answer, this is the very little code that you would need to do this interaction. I'm assuming a slider in this case. The blog post shows how to use it with scroll events and if you want to do it with gestures then I'm sure you can figure it out :)

In you animation setup (note that I'm using the CATransform3D instead of CGAffineTransform)

CABasicAnimation *yourAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
yourAnimation.duration  = 1.; // For convenience (so that timeOffset is from 0.0 to 1.0)
yourAnimation.fromValue = [NSValue valueWithCATransform3D:fromTransform];
yourAnimation.toValue   = [NSValue valueWithCATransform3D:toTransform];
[self.yourView.layer addAnimation: yourAnimation forKey:@"Your Animation"];

self.yourView.layer.speed = 0.0; // Pause every animation on that layer

And the slider to drive the interaction of the animation (slider is configured to go from 0.0 to 1.0):

- (IBAction)sliderChanged:(UISlider *)sender {
    self.yourView.layer.timeOffset = sender.value; // Change the "current time" of the animation
}
Community
  • 1
  • 1
David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205