CABasicAnimation - transform scale keep in center

Leon Storey picture Leon Storey · May 8, 2013 · Viewed 33.1k times · Source

Trying to animatie an ellipse masked on a UIView to be scale transformed remaining in the center position.

I have found CALayer - CABasicAnimation not scaling around center/anchorPoint, and followed by adding a bounds property to the maskLayer CAShapeLayer however, this ends with the mask being positioned in the left corner with only 1/4 of it showing. I would like the mask to remain within the center of the screen.

@synthesize maskedView;
- (void)viewDidLoad
{
    [super viewDidLoad];
    UIViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"Next"];

    vc.view.frame = CGRectMake(0,0, self.view.frame.size.width, self.view.frame.size.height);
vc.view.layer.bounds = self.view.layer.bounds;

    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];

    CGRect maskRect = CGRectMake((self.view.frame.size.width/2)-50, (self.view.frame.size.height/2)-50, 100, 100);
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddEllipseInRect(path, nil, maskRect);
    [maskLayer setPath:path];

    CGPathRelease(path);

    vc.view.layer.mask = maskLayer;
    [self.view addSubview:vc.view];

    maskedView = vc.view;
    [self startAnimation];
}

Animation...

-(void)startAnimation
{
    maskedView.layer.mask.bounds = CGRectMake((self.view.frame.size.width/2)-50, (self.view.frame.size.height/2)-50, 100, 100);
    maskedView.layer.mask.anchorPoint = CGPointMake(.5,.5);
    maskedView.layer.mask.contentsGravity = @"center";

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];

    animation.duration = 1.0f;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animation.fromValue = [NSNumber numberWithFloat:1.0f];
    animation.toValue = [NSNumber numberWithFloat:5.0f];

    [maskedView.layer.mask addAnimation:animation forKey:@"animateMask"];
}

Update

I seem to have fixed this with a second animation on the position key. Is this correct or is there a better way of doing this?

CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"position"];

animation2.duration = 1.0f;

animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

animation2.fromValue = [NSValue valueWithCGPoint:CGPointMake((self.view.frame.size.width/2), (self.view.frame.size.height/2))];
animation2.toValue = [NSValue valueWithCGPoint:CGPointMake((self.view.frame.size.width/2), (self.view.frame.size.height/2))];

[toBeMask.layer.mask addAnimation:animation2 forKey:@"animateMask2"];

Answer

nevyn picture nevyn · Dec 24, 2014

You can create a scale around the center of the object instead of (0, 0) like this:

CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D tr = CATransform3DIdentity;
tr = CATransform3DTranslate(tr, self.bounds.size.width/2, self.bounds.size.height/2, 0);
tr = CATransform3DScale(tr, 3, 3, 1);
tr = CATransform3DTranslate(tr, -self.bounds.size.width/2, -self.bounds.size.height/2, 0);
scale.toValue = [NSValue valueWithCATransform3D:tr];

So we start out with the "identity" transform (which means 'no transform'). Then we translate (move) to the center of the object. Then we scale. Then we move back to origo so we're back where we started (we only wanted to affect the scale operation).