6

How to shift shape (from oval to rect and vice versa) animated?

Dirty code:

UIBezierPath *roundedRectBezierPath = [UIBezierPath bezierPathWithRoundedRect:newRect cornerRadius:10];
UIBezierPath *ovalBezierPath = [UIBezierPath bezierPathWithOvalInRect:newRect];

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.duration = 3;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

if (isRect) {

    self.shapeLayer.path = roundedRectBezierPath.CGPath;
    animation.fromValue = (__bridge id)(ovalBezierPath.CGPath);
    animation.toValue = (__bridge id)(roundedRectBezierPath.CGPath);

} else {

    self.shapeLayer.path = ovalBezierPath.CGPath;
    animation.fromValue = (__bridge id)(roundedRectBezierPath.CGPath);
    animation.toValue = (__bridge id)(ovalBezierPath.CGPath);

}    

[self.shapeLayer addAnimation:animation forKey:@"animatePath"];

The result is weird:

Bad animation

I expect shape to shrink/expand, not weird redraw.

user500
  • 4,519
  • 6
  • 43
  • 56
  • LOL! Looks like the rect is being drawn [top left, top right, bottom right, bottom left] but the ellipse is being drawn anti-clockwise from the "3 o'clock" position. The animation will map points on the path from the square to the circle. i.e. top left on rect goes to right on circle. try rotating the circle path by (4 * PI) / 3 rads counter-clockwise. – Fogmeister Jul 02 '13 at 15:42
  • Still not expected animation. – user500 Jul 03 '13 at 05:04
  • @user620297 On the contrary. I expect just that behavior. From the docs: "If the two paths have a different number of control points or segments the results are undefined". – David Rönnqvist Jul 03 '13 at 07:49
  • @DavidRönnqvist You are right. So how to do it? – user500 Jul 03 '13 at 08:04

1 Answers1

12

If you look at the documentation this is exactly the behavior that you should expect. From the docs:

If the two paths have a different number of control points or segments the results are undefined.

You can get around this in two ways: either

  • create the paths yourself in a way that you can guarantee that they both have the same number of paths
  • create a custom animation for the path transition yourself.

Create paths with same number of segments and control points

For the first option I would use addArcWithCenter:radius:startAngle:endAngle:clockwise: four times to make up the rounded rect. (It will automatically add the straight lines to your path). You can read more about the construction of Bézier paths in "Thinking like Bézier paths" (written by me). There are some interactive elements there that should help you figure out how adding arcs to a path works.      

Create a custom path animation

The other option is to create the paths any way you want with different numbers of control points and/or segments and do the animation yourself. This is an excellent explanation of how to do your own path animations with shape layers.

David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205
  • A little more than two years later, this answer still helps. The best part of this answer: "Create the paths yourself in a way that you can guarantee that they both have the same number of paths." In my case, I change the oval to be a rounded rect with the appropriate cornerRadius. The animation works perfectly now. – C Fairweather Jul 21 '15 at 05:36