How to animate the frame of an layer with CABasicAnimation?
The frame property of a CALayer is a derived property, dependent on the position, anchorPoint, bounds and transform of the layer. Instead of animating the frame, you should instead animate the position or bounds, depending on what effect you are trying to accomplish.
To move a layer, you can animate the position
:
-(void)moveLayer:(CALayer*)layer to:(CGPoint)point
{
// Prepare the animation from the current position to the new position
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [layer valueForKey:@"position"];
// NSValue/+valueWithPoint:(NSPoint)point is available on Mac OS X
// NSValue/+valueWithCGPoint:(CGPoint)point is available on iOS
// comment/uncomment the corresponding lines depending on which platform you're targeting
// Mac OS X
animation.toValue = [NSValue valueWithPoint:NSPointFromCGPoint(point)];
// iOS
//animation.toValue = [NSValue valueWithCGPoint:point];
// Update the layer's position so that the layer doesn't snap back when the animation completes.
layer.position = point;
// Add the animation, overriding the implicit animation.
[layer addAnimation:animation forKey:@"position"];
}
To resize a layer, you would animate the bounds
parameter:
-(void)resizeLayer:(CALayer*)layer to:(CGSize)size
{
// Prepare the animation from the old size to the new size
CGRect oldBounds = layer.bounds;
CGRect newBounds = oldBounds;
newBounds.size = size;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"];
// NSValue/+valueWithRect:(NSRect)rect is available on Mac OS X
// NSValue/+valueWithCGRect:(CGRect)rect is available on iOS
// comment/uncomment the corresponding lines depending on which platform you're targeting
// Mac OS X
animation.fromValue = [NSValue valueWithRect:NSRectFromCGRect(oldBounds)];
animation.toValue = [NSValue valueWithRect:NSRectFromCGRect(newBounds)];
// iOS
//animation.fromValue = [NSValue valueWithCGRect:oldBounds];
//animation.toValue = [NSValue valueWithCGRect:newBounds];
// Update the layer's bounds so the layer doesn't snap back when the animation completes.
layer.bounds = newBounds;
// Add the animation, overriding the implicit animation.
[layer addAnimation:animation forKey:@"bounds"];
}
You can combine these animations using a CAAnimationGroup if you need to move and resize a layer at the same time.
we can change the properties of "bounds" and "position" to animate it, such as
-(void)handleTap2:(UITapGestureRecognizer *)recognizer {
UIImageView *vw = (UIImageView *)[recognizer view];
CGPoint startPoint = CGPointMake(vw.frame.size.width/2+vw.frame.origin.x, vw.frame.size.height/2+vw.frame.origin.y);
CGPoint endPoint = CGPointMake(160, 240);
CGRect startBounds = vw.bounds;
CGRect stopBounds = self.view.bounds;
layer = [CALayer layer];
layer.frame = self.view.frame;
layer.contents = (id)[vw.image CGImage];
[self.view.window.layer addSublayer:layer];
CABasicAnimation * baseAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
baseAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
baseAnimation.fromValue = [NSValue valueWithCGPoint:startPoint] ;
baseAnimation.toValue = [NSValue valueWithCGPoint:endPoint] ;
CABasicAnimation * boundsAnimation = [CABasicAnimation animationWithKeyPath:@"bounds"];
boundsAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
boundsAnimation.fromValue = [NSValue valueWithCGRect:startBounds] ; boundsAnimation.toValue = [NSValue valueWithCGRect:stopBounds] ;
CAAnimationGroup * group =[CAAnimationGroup animation];
group.removedOnCompletion=NO; group.fillMode=kCAFillModeForwards;
group.animations =[NSArray arrayWithObjects:baseAnimation, boundsAnimation, nil];
group.duration = 0.7;
[layer addAnimation:group forKey:@"frame"];
}
The question is antique, but I will answer it anyway.
Frame property is not animatable. You have to animate other properties. Also you have to disable implicit animations.
let updatedBounds = ...
let animation = CABasicAnimation(keyPath: "bounds")
animation.duration = 0.5
//it's better to start animation from presentation layer in case there is already animation going on
animation.fromValue = customLayer.presentation()?.bounds
animation.toValue = updatedBounds
customLayer.add(animation, forKey: nil)
//disable implicit animation for thoose properties
CATransaction.begin()
CATransaction.setDisableActions(true)
//update properties so they will be updated at the end of animation
customLayer.bounds = updatedBounds
customLayer.position = originalRect.origin
customLayer.anchorPoint = CGPoint(x: 0, y: 0)
CATransaction.commit()