I'm trying to animation the transition of a CALayer from normal corners to rounded corners. I only want to round the top corners, so I am using a UIBezierPath. Here's the code:
UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;
UIBezierPath* newPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(8.0f, 8.0f)];
UIBezierPath* oldPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(0.0f, 0.0f)];
CAShapeLayer* layer = [CAShapeLayer layer];
layer.frame = self.bounds;
layer.path = oldPath.CGPath;
self.layer.mask = layer;
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:@"path"];
[a setDuration:0.4f];
[a setFromValue:(id)layer.path];
[a setToValue:(id)newPath.CGPath];
layer.path = newPath.CGPath;
[layer addAnimation:a forKey:@"path"];
But when I run this, the corners are rounded, but there is no animation- it happens instantly. I have seen some similar questions here on SO, but they didn't solve my problem.
Animating CALayer's shadowPath property
I'm sure that I'm just doing one little thing wrong which is causing the problem, but for the life of me I can't figure it out.
Solution:
- (void)roundCornersAnimated:(BOOL)animated {
UIRectCorner corners = UIRectCornerTopLeft | UIRectCornerTopRight;
UIBezierPath* newPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(8.0f, 8.0f)];
UIBezierPath* oldPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:CGSizeMake(0.0001f, 0.0001f)];
CAShapeLayer* layer = [CAShapeLayer layer];
layer.frame = self.bounds;
self.layer.mask = layer;
if(animated) {
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:@"path"];
[a setDuration:0.4f];
[a setFromValue:(id)oldPath.CGPath];
[a setToValue:(id)newPath.CGPath];
layer.path = newPath.CGPath;
[layer addAnimation:a forKey:@"path"];
} else {
layer.path = newPath.CGPath;
}
}
Really all I needed to do was set a proper 'from' value for the animation.
It looks to me like you are creating a shape layer that does not have a path, then trying to animate adding a path to it. Shape layers are special case, and don't work that way.
You have to have a path in the shape layer before and after the animation, and the path needs to have the same number of points in it both before and after.
You would need to create a rounded rectangle path with a corner radius of 0 (manually, using a series of sides and corner arcs), and then build a new path with non zero radii for the corners you want to round and install that path as your animation.
Even that might not work, though, because the path needs to have exactly the same number of points in it, and I suspect that the arcs would simplify to points when the radius is zero.
I just found a UIBezierPath call that will create a rectangle where you can select which corners to round. The call is bezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
That will create a rectangle with as many rounded corners as you like. However, I'm not sure if it will work for path animation. You might need to request all corners be rounded with the 2 non-rounded corners set to zero radius. That way you MIGHT get a path with the same number of points in it as a rounded rectangle path so the path animation works correctly. You'll have to try it.