I'm trying to animate the bounds of a CALayer and it's not working. Here's the code:
class CircleView < UIIVew
def initWithFrame(frame)
super
# determine the dimensions
radius = (bounds.size.width < bounds.size.height ? bounds.size.width : bounds.size.height) / 2
# create the circle layer
@circle = CAShapeLayer.layer
@circle.bounds = bounds
@circle.path = UIBezierPath.bezierPathWithRoundedRect(bounds, cornerRadius: radius).CGPath
@circle.fillColor = UIColor.blueColor.CGColor
# add the circle to the view
self.layer.addSublayer(@circle)
shrink
self
end
private
# Applies the shrink animation to the provided circle layer.
def shrink
# determine the bounds
old_bounds = @circle.bounds
new_bounds = CGRectMake(old_bounds.size.width / 2, old_bounds.size.height / 2, 0, 0)
# animate the shrinking
animation = CABasicAnimation.animationWithKeyPath("bounds")
animation.fromValue = NSValue.valueWithCGRect(old_bounds)
animation.toValue = NSValue.valueWithCGRect(new_bounds)
animation.duration = 3.0
@circle.addAnimation(animation, forKey:"bounds")
# set the bounds so the animation doesn't bounce back when it's complete
@circle.bounds = new_bounds
end
# Applies the shrink animation to the provided circle layer.
def disappear
# animate the shirnk
animation = CABasicAnimation.animationWithKeyPath("opacity")
animation.fromValue = NSNumber.numberWithFloat(1.0)
animation.toValue = NSNumber.numberWithFloat(0.0)
animation.duration = 3.0
@circle.addAnimation(animation, forKey:"opacity")
# set the value so the animation doesn't bound back when it's completed
@circle.opacity = 0.0
end
end
If I call disappear from the initWithFrame
method, the animation works fine. However, calling shrink does nothing. What am I doing wrong?
That's because you are using a CAShapeLayer
. The path
is independent of the bounds
, so you have to animate it separately.
I could reproduce your problem with a version of your code roughly translated to Objective C. When I changed the shrink
method to this, it worked:
-(void)shrink
{
CGRect old_bounds = self.circle.bounds;
CGRect new_bounds = CGRectMake(old_bounds.size.width / 2, old_bounds.size.height / 2, 0, 0);
// bounds
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: @"bounds"];
animation.fromValue = [NSValue valueWithCGRect: old_bounds];
animation.toValue = [NSValue valueWithCGRect: new_bounds];
animation.duration = 3.0;
[self.circle addAnimation: animation forKey: @"bounds"];
// path
CGPathRef old_path = self.circle.path;
CGPathRef new_path = [UIBezierPath bezierPathWithRoundedRect:new_bounds cornerRadius:0].CGPath;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath: @"path"];
pathAnimation.fromValue = (__bridge id)(old_path);
pathAnimation.toValue = (__bridge id)(new_path);
pathAnimation.duration = 3.0;
[self.circle addAnimation: pathAnimation forKey: @"path"];
//set the value so the animation doesn't bound back when it's completed
self.circle.bounds = new_bounds;
self.circle.path = new_path;
}
Technically, you might not even have to animate the bounds. If you set a distinct backgroundColor
on your circle
layer, you can experiment a little and more easily see how bounds
and path
can be set and animated more or less independently (for example, in your original code you could see that the bounds are in fact animated but the path stays the same).