Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CABasicAnimation with CALayer path doesn't animate

Tags:

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.

iPhone CALayer animation

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.

like image 800
eric.mitchell Avatar asked Jul 13 '12 02:07

eric.mitchell


2 Answers

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.

like image 124
Duncan C Avatar answered Oct 20 '22 22:10

Duncan C


Try to put:

layer.path = newPath.CGPath;

after:

[layer addAnimation:a forKey:@"path"];
like image 37
demastyle Avatar answered Oct 20 '22 20:10

demastyle