5

I am trying to animate the width of a rounded rectangle, the problem is when going from bigger width to thinner width, the animation does an "aberration ease jump".

Here's the code:

shapeLayer = [CAShapeLayer layer];
shapeRect = CGRectMake(0.0f, 0.0f, 150.0f, 200.0f);
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake(iniPosX, 80.0f)];
[shapeLayer setFillColor:[[UIColor blackColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor clearColor] CGColor]];
[shapeLayer setLineWidth:1.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setOpacity:0.2];
path = [UIBezierPath bezierPathWithRoundedRect:shapeRect cornerRadius:15.0];
[shapeLayer setPath:path.CGPath];
[self.layer addSublayer:shapeLayer];

And when I start the animation:

- (void)adjustSelectorToPosAndSize:(float)posX andWidth:(float)width
{
    shapeRect = CGRectMake(0.0f, 0.0f, width, 200.0f);
    [shapeLayer setBounds:shapeRect];
    [shapeLayer setPosition:CGPointMake(posX, 80.0f)];
    path = [UIBezierPath bezierPathWithRoundedRect:shapeRect cornerRadius:15.0];
    [shapeLayer setPath:path.CGPath];
}

What am I doing wrong?

WendiKidd
  • 4,333
  • 4
  • 33
  • 50
murb83
  • 325
  • 3
  • 14

2 Answers2

3

Although CAShapeLayer’s path property is animatable, it does not do so implicitly like bounds and position, you have to make an explicit animation:

CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath: @"path"];
anim.fromValue = (id) fromPath.CGPath;
anim.toValue = (id) toPath.CGPath;
anim.duration = 1.0;
[layer addAnimation: anim forKey: @“resize”];

Since you probably want all three animations to happen together, you can make them all explicit and grouped together:

CABasicAnimation* anim1 = [CABasicAnimation animationWithKeyPath: @"position"];
anim1.fromValue = [NSValue valueWithCGPoint: fromPosition];
anim1.toValue = [NSValue valueWithCGPoint: toPosition];
anim1.duration = 1.0;

CABasicAnimation* anim2 = [CABasicAnimation animationWithKeyPath: @"bounds"];
anim2.fromValue = [NSValue valueWithCGRect: fromBounds];
anim2.toValue = [NSValue valueWithCGRect: toBounds];
anim2.duration = 1.0;

CABasicAnimation* anim3 = [CABasicAnimation animationWithKeyPath: @"path"];
anim3.fromValue = (id) fromPath.CGPath;
anim3.toValue = (id) toPath.CGPath;
anim3.duration = 1.0;

CAAnimationGroup* animG = [CAAnimationGroup animation];
animG.animations = [NSArray arrayWithObjects: anim1, anim2, anim3, nil];
animG.duration = 1.0;
[layer addAnimation: animG forKey:@"resize"];

If your shape does not have contents or children then you may be able to use a CALayer instead:

CALayer* rectLayer = [CALayer layer];
rectLayer.masksToBounds = YES;
rectLayer.bounds = startBounds;
rectLayer.backgroundColor = [[UIColor blackColor] CGColor];
rectLayer.cornerRadius = 15.0;
[self.layer addSublayer: rectLayer];

// Then later implicitly animate the bounds:
rectLayer.bounds = newBounds;
Jeff Argast
  • 1,241
  • 10
  • 8
  • Thanks!! I am testing your code, I tell you when you finish testing. – murb83 Jun 25 '12 at 11:53
  • I'm Sorry, but doesn't work well, I think i'm doing something wrong, but layer doesn't moves to where i want, i think the reference point is different when i do the animation. Sorry my english is not good. The way that you uses the animations, is really of my interest, you opened to me a new way for me in group animations. ^^ – murb83 Jun 25 '12 at 14:30
3

Doing this with a CAShapeLayer seems overly complicated to me if you are only using it for a rounded rectangle. Why not simply use a CALayer with the cornerRadius properly set?

Animating the frame with cornerRadius set will work fine.

myLayer = [CALayer layer];
shapeRect = CGRectMake(0.0f, 0.0f, 150.0f, 200.0f);
[myLayer setBounds:shapeRect];
[myLayer setPosition:CGPointMake(iniPosX, 80.0f)];
[myLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
[myLayer setBorderColor:[[UIColor clearColor] CGColor]];
[myLayer setBorderWidth:1.0f];
[myLayer setOpacity:0.2];
[myLayer setCornerRadius:15.0];
[self.layer addSublayer:myLayer];

Animating it is done by simply chaining the frame (not the path)

- (void)adjustSelectorToPosAndSize:(float)posX andWidth:(float)width
{
    shapeRect = CGRectMake(0.0f, 0.0f, width, 200.0f);
    [myLayer setBounds:shapeRect];
    [myLayer setPosition:CGPointMake(posX, 80.0f)];
}

Important:

Some of the properties changed name like fillColor became backgroundColor and the strokeColor and lineWidth became borderColor and borderWidth etc.

Community
  • 1
  • 1
David Rönnqvist
  • 56,267
  • 18
  • 167
  • 205