I would like to animate a figure when it moves from a circle to a triangle with a rounded corner.
TL; DR: How do I animate the CAShapeLayer path between two forms of CGPath ? I know that they must have the same number of breakpoints, but I think I am doing this - what is wrong with this code?
The initial and final states will look something like this: transition http://clrk.it/4vJS+
Here is what I have tried so far: I am using CAShapeLayer and animating the changes to the path property.
According to the documentation (primary focus):
The path object can be animated using any of the specific subclasses of CAPropertyAnimation . Paths will interpolate as a linear mixture of on-line points; โautonomousโ points can be interpolated nonlinearly (for example, to maintain the continuity of the derivative curve). If two paths have a different number of control points or segments, the results are undefined.
In an attempt to make the circle and triangle have the same number of control points, I made them like four-pointed shapes: the circle is a strongly rounded rectangle, and the triangle โhidesโ the fourth control point on one side.
Here is my code:
self.view.backgroundColor = .blueColor() //CREATE A CASHAPELAYER let shape = CAShapeLayer() shape.frame = CGRect(x: 50, y: 50, width: 200, height: 200) shape.fillColor = UIColor.redColor().CGColor self.view.layer.addSublayer(shape) let bounds = shape.bounds //CREATE THE SQUIRCLE let squareRadius: CGFloat = CGRectGetWidth(shape.bounds)/2 let topCorner = CGPointMake(bounds.midX, bounds.minY) let rightCorner = CGPointMake(bounds.maxX, bounds.midY) let bottomCorner = CGPointMake(bounds.midX, bounds.maxY) let leftCorner = CGPointMake(bounds.minX, bounds.midY) let squarePath = CGPathCreateMutable() let squareStartingPoint = midPoint(leftCorner, point2: topCorner) CGPathMoveToPoint(squarePath, nil, squareStartingPoint.x, squareStartingPoint.y) addArcToPoint(squarePath, aroundPoint: topCorner, onWayToPoint: rightCorner, radius: squareRadius) addArcToPoint(squarePath, aroundPoint: rightCorner, onWayToPoint: bottomCorner, radius: squareRadius) addArcToPoint(squarePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: squareRadius) addArcToPoint(squarePath, aroundPoint: leftCorner, onWayToPoint: topCorner, radius: squareRadius) CGPathCloseSubpath(squarePath) let square = UIBezierPath(CGPath: squarePath) //CREATE THE (FAKED) TRIANGLE let triangleRadius: CGFloat = 25.0 let trianglePath = CGPathCreateMutable() let triangleStartingPoint = midPoint(topCorner, point2: rightCorner) let startingPoint = midPoint(topCorner, point2: leftCorner) CGPathMoveToPoint(trianglePath, nil, startingPoint.x, startingPoint.y) let cheatPoint = midPoint(topCorner, point2:bottomCorner) addArcToPoint(trianglePath, aroundPoint: topCorner, onWayToPoint: cheatPoint, radius: triangleRadius) addArcToPoint(trianglePath, aroundPoint: cheatPoint, onWayToPoint: bottomCorner, radius: triangleRadius) addArcToPoint(trianglePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: triangleRadius) addArcToPoint(trianglePath, aroundPoint: leftCorner, onWayToPoint: topCorner, radius: triangleRadius) CGPathCloseSubpath(trianglePath) let triangle = UIBezierPath(CGPath: trianglePath) shape.path = square.CGPath
... and later, in viewDidAppear :
let animation = CABasicAnimation(keyPath: "path") animation.fromValue = self.square.CGPath animation.toValue = self.triangle.CGPath animation.duration = 3 self.shape.path = self.triangle.CGPath self.shape.addAnimation(animation, forKey: "animationKey")
I have two quick little functions for clearer code:
func addArcToPoint(path: CGMutablePath!, aroundPoint: CGPoint, onWayToPoint: CGPoint, radius: CGFloat) { CGPathAddArcToPoint(path, nil, aroundPoint.x, aroundPoint.y, onWayToPoint.x, onWayToPoint.y, radius) } func midPoint(point1: CGPoint, point2: CGPoint) -> CGPoint { return CGPointMake((point1.x + point2.x)/2, (point1.y + point2.y)/2) }
My current result is as follows:

NOTE. . I am trying to build a circle from a very rounded square, trying to get the same number of control points in vector form. In the above GIF, the angle radius has been reduced to make the conversion more noticeable.
What do you think is going on? How else can I achieve this effect?