27
votes

Using the code below I've been trying to get my CALayer to scale around its center - but it scales by left/top (0,0). I saw this question: Anchor Point in CALayer and have tried setting the anchorPoint - but it was [.5, .5] before I set it and setting makes no difference. Also tried setting contentsGravity.

The set up is I have a CAShapeLayer added to self.view.layer I do some drawing in it and then add the CABasicAnimation. The animation runs fine - but it scales toward the top/left - not the center of the view.

Have been tinkering with this for a few hours - it must be something simple but I'm not getting it.

shapeLayer.anchorPoint = CGPointMake(.5,.5);
shapeLayer.contentsGravity = @"center";

printf("anchorPoint %f  %f\n", shapeLayer.anchorPoint.x, shapeLayer.anchorPoint.y);

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

animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
animation.toValue =   [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];

[animation setDuration:1.0];
animation.fillMode=kCAFillModeForwards;
animation.removedOnCompletion=NO;
[shapeLayer addAnimation:animation forKey:@"zoom"];
4

4 Answers

56
votes

What is the bounds of your layer?

I bet it has zero size; try setting its size to the same size as your shape.

CAShapeLayer draws the shape as kind of an overlay, independent of the contents (and bounds and contentsGravity and etc.) that you'd use in an ordinary CALayer. It can be a bit confusing.

5
votes

Also make sure that you're adding the animation after the view is created. If you're trying to add animation in the viewDidLoad, it probably won't work. Try adding it to viewDidAppear instead.

I only know Swift, but here's an example:

override func viewDidAppear (animated: Bool) {
    addPulsation(yourButton)
}

func addPulsation (button: UIButton) {
    let scaleAnimation:CABasicAnimation = CABasicAnimation(keyPath: "transform.scale")
    scaleAnimation.duration = 1.0
    scaleAnimation.repeatCount = 100
    scaleAnimation.autoreverses = true
    scaleAnimation.fromValue = 1.05;
    scaleAnimation.toValue = 0.95;
    button.layer.addAnimation(scaleAnimation, forKey: "scale")
}
1
votes

It's an old thread but in case this helps anyone.

This ia a coordinate space problem. You just need to set the drawing space and the space of the the path. This code works for me...

...just put this at the start of the animation creation method (inside a method on the parent view)...

// add animation to remap (without affecting other elements)
    let animationLayer = CALayer()
    layer?.addSublayer(animationLayer). // (don't use optional for iOS)

// set the layer origin to the center of the view (or any center point for the animation)
    animationLayer.bounds.origin .x = -self.bounds.size.width / 2.0
    animationLayer.bounds.origin .y = -self.bounds.height / 2.0

// make image layer with circle around the zero point
    let imageLayer = CAShapeLayer()
    animationLayer.addSublayer(imageLayer)

    let shapeBounds = CGRect(x: -bounds.size.width/2.0,
                             y:-bounds.size.height/2.0,
                             width: bounds.size.width,
                             height: bounds.size.height)

    imageLayer.path = CGPath(ellipseIn: shapeBounds, transform: nil)
    imageLayer.fillColor = fillColour


    // **** apply your animations to imageLayer here ****

this code works unchanged on iOS and MacOS - apart from the optional needed on layer for MacOS.

(BTW you need to remove the animation layer after the animation finishes!)

-1
votes

I spent hours to find out how to do it according the center but couldn't find any solutions for OSX. I decided to to move layer using CATransform3DMakeTranslation and combine both transforms.

My code:

NSView *viewToTransform = self.view;
[viewToTransform setWantsLayer:YES];

viewToTransform.layer.sublayerTransform = CATransform3DConcat(CATransform3DMakeScale(-1.0f, -1.0f, 1.0f), CATransform3DMakeTranslation(viewToTransform.bounds.size.width, viewToTransform.bounds.size.height, 0));