iOS animate/morph shape from circle to square

animation, cabasicanimation, calayer, cashapelayer, ios

Solution

After playing with it for a little while in the playground I noticed that each arc adds two segments to the bezier path, not just one. Moreover, calling `closePath` adds two more. So to keep the number of segments consistent, I ended up with adding fake segments to my square path. The code is in Swift, but I think it doesn't matter.

func circlePathWithCenter(center: CGPoint, radius: CGFloat) -> UIBezierPath {
    let circlePath = UIBezierPath()
    circlePath.addArcWithCenter(center, radius: radius, startAngle: -CGFloat(M_PI), endAngle: -CGFloat(M_PI/2), clockwise: true)
    circlePath.addArcWithCenter(center, radius: radius, startAngle: -CGFloat(M_PI/2), endAngle: 0, clockwise: true)
    circlePath.addArcWithCenter(center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI/2), clockwise: true)
    circlePath.addArcWithCenter(center, radius: radius, startAngle: CGFloat(M_PI/2), endAngle: CGFloat(M_PI), clockwise: true)
    circlePath.closePath()
    return circlePath
}

func squarePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
    let squarePath = UIBezierPath()
    let startX = center.x - side / 2
    let startY = center.y - side / 2
    squarePath.moveToPoint(CGPoint(x: startX, y: startY))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.addLineToPoint(CGPoint(x: startX + side, y: startY))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.addLineToPoint(CGPoint(x: startX + side, y: startY + side))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.addLineToPoint(CGPoint(x: startX, y: startY + side))
    squarePath.addLineToPoint(squarePath.currentPoint)
    squarePath.closePath()
    return squarePath
}

Problem

I try to change a circle into a square and vice versa and I am almost there. However it doesn't animates as expected. I would like all corners of a square to be animated/morphed at the same time but what I get is the following: I use `CAShapeLayer` and `CABasicAnimation` in order to animate shape property. Here is how I create the circle path: ``` - (UIBezierPath *)circlePathWithCenter:(CGPoint)center radius:(CGFloat)radius { UIBezierPath *circlePath = [UIBezierPath bezierPath]; [circlePath addArcWithCenter:center radius:radius startAngle:0 endAngle:M_PI/2 clockwise:YES]; [circlePath addArcWithCenter:center radius:radius startAngle:M_PI/2 endAngle:M_PI clockwise:YES]; [circlePath addArcWithCenter:center radius:radius startAngle:M_PI endAngle:3*M_PI/2 clockwise:YES]; [circlePath addArcWithCenter:center radius:radius startAngle:3*M_PI/2 endAngle:M_PI clockwise:YES]; [circlePath closePath]; return circlePath; } ``` Here is the square path: ``` - (UIBezierPath *)squarePathWithCenter:(CGPoint)center size:(CGFloat)size { CGFloat startX = center.x-size/2; CGFloat startY = center.y-size/2; UIBezierPath *squarePath = [UIBezierPath bezierPath]; [squarePath moveToPoint:CGPointMake(startX, startY)]; [squarePath addLineToPoint:CGPointMake(startX+size, startY)]; [squarePath addLineToPoint:CGPointMake(startX+size, startY+size)]; [squarePath addLineToPoint:CGPointMake(startX, startY+size)]; [squarePath closePath]; return squarePath; } ``` I apply the circle path to one of my View's layers, set the fill etc. It draws perfectly. Then in my gestureRecognizer selector I create and run the following animation: ``` CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"]; animation.duration = 1; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; animation.fromValue = (__bridge id)(self.stateLayer.path); animation.toValue = (__bridge id)(self.stopPath.CGPath); self.stateLayer.path = self.stopPath.CGPath; [self.stateLayer addAnimation:animation forKey:@"animatePath"]; ``` As you can notice in the `circlePathWithCenter:radius:` and `squarePathWithCenter:size:` I follow the suggestion from here (to have the same number of segments and control points): Smooth shape shift animation The animation looks better then in the post from above but it is still not the one I want to achieve :( I know that I can do it with simple `CALayer` and then set appropriate level of `cornerRadius` to make a circle out of square/rectangle and after that animate `cornerRadius` property to change it from circle to square but I'm still extremaly curious if it is even possible to do it with `CAShapeLayer` and `path` animation. Thanks in advance for help!

Original source

Related problems