CABasicAnimation: transformar la escala en el centro

Intentando animar una elipse enmascarada en una UIView a escala transformada restante en la position central.

He encontrado que CALayer – CABasicAnimation no escala en el centro / anchorPoint , y seguido por la adición de una propiedad de bounds a maskLayer CAShapeLayer , sin embargo, esto termina con la máscara colocada en la esquina izquierda con solo 1/4 de la misma. Me gustaría que la máscara permanezca en el centro de la pantalla.

 @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]; } 

Animación…

 -(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"]; } 

Actualizar

Parece que lo he arreglado con una segunda animation en la tecla de position . ¿Es esto correcto o hay una mejor manera de hacerlo?

 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"]; 

Puede crear una escala alnetworkingedor del centro del object en lugar de (0, 0) así:

 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]; 

Entonces comenzamos con la transformación de "identidad" (que significa "sin transformación"). Luego traducimos (movemos) al centro del object. Luego escalamos. Luego volvemos a origo, así que estamos de return donde comenzamos (solo queríamos afectar la operación de escala).

Parece que lo he arreglado con una segunda animation en la tecla de position. ¿Es esto correcto o hay una mejor manera de hacerlo?

 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"]; 

No estoy seguro de por qué, pero CAShapeLayer no establece la propiedad de límites en la capa. En mi caso, ese era el problema. Intente establecer los límites. Eso debería funcionar.

Hice algo así para build el círculo inicial

 self.circle = [CAShapeLayer layer]; CGRect roundedRect = CGRectMake(0, 0, width, width); self.circle.bounds = roundedRect; UIBezierPath *start = [UIBezierPath bezierPathWithRoundedRect:roundedRect cornerRadius:width/2]; [self.circle setPath:start.CGPath]; self.circle.position = CGPointMake(whatever suits you); self.circleView.layer.mask = self.circle; [self.circleView.layer.mask setValue: @(1) forKeyPath: @"transform.scale"]; 

Y algo así para escalarlo

 CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; scale.fromValue = [self.circleView.layer.mask valueForKeyPath:@"transform.scale"]; scale.toValue = @(100); scale.duration = 1.0; scale.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [self.circleView.layer.mask setValue:scale.toValue forKeyPath:scale.keyPath]; [self.circleView.layer.mask addAnimation:scale forKey:scale.keyPath]; 

PD: AnchorPoint es de forma pnetworkingeterminada (.5, .5) según las documentaciones de apple … pero todos sabemos que eso no significa nada correcto.

¡¡Espero eso ayude!!

He escrito la siguiente function con muchos ajustes y opciones adicionales, podría ser útil para muchos otros.

 func layerScaleAnimation(layer: CALayer, duration: CFTimeInterval, fromValue: CGFloat, toValue: CGFloat) { let timing = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) let scaleAnimation: CABasicAnimation = CABasicAnimation(keyPath: "transform.scale") CATransaction.begin() CATransaction.setAnimationTimingFunction(timing) scaleAnimation.duration = duration scaleAnimation.fromValue = fromValue scaleAnimation.toValue = toValue layer.add(scaleAnimation, forKey: "scale") CATransaction.commit() } 

Nota: No olvide actualizar el tamaño después de completar la animation, ya que CATransaction vuelve al tamaño real.