CABasicAnimation does not animate my property

I tried to figure out what was wrong with my animation, and I still haven't figured it out. I think it should be really straightforward, but something is probably missing me, even after reading a lot of examples and documentation.

My problem initially is that on iPhone you cannot resize layers automatically (with a view). The documentation says otherwise, but there is no autoresizeMask file for the layer in the SDK. Therefore, I decided to make a small workaround and animate the layer myself.

I have this simple piece of code that should make a simple size animation. The values ​​are good, and I even assigned a delegate to track if the animation starts / ends.

// I've got a property named layerImage (which is a CALayer) - (void)animateTestWithFrame:(CGRect)value { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"layerImage.frame"]; animation.duration = 1; animation.fromValue = [NSValue valueWithCGRect:self.frame]; animation.toValue = [NSValue valueWithCGRect:value]; animation.removedOnCompletion = YES; animation.delegate = self; [self.layer addAnimation:animation forKey:@"layerImage.frame"]; } 

So, any ideas? (This view, which contains the code, is a subview of the window subzone, if that may matter)

--- EDIT ---

It seems that the frame is not animating with CABasicAnimation and the named property "frame". When using borders, I have some strange result, but at least I get something. Continue the investigation in this regard.

+7
properties iphone animation core-animation
source share
3 answers

So good that you figured it out here, but your answer to your question has some inaccuracies. Let me fix a few things:

The frame property can be animated - just not with explicit animation. If you do the following on a layer other than the root layer of the view, the frame will animate just fine:

 [CATransaction begin]; [CATransaction setAnimationDuration:2.0f]; [animationLayer setFrame:CGRectMake(100.0f, 100.0f, 100.0f, 100.0f)]; [CATransaction commit]; 

Remember that setting a property on a layer will animate this property by default. In fact, you need to disable the animation when changing properties, if you do not want it to be animated. (Of course, this is only true if you are animating a layer other than the root level of the view.) You use animation correctly both in position and in borders if you need to use explicit animation.

You can animate a frame on a UIView using implicit animation:

 [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:3.0f]; [[self view] setFrame:CGRectMake(45.0f, 45.0f, 100.0f, 100.0f)]; [UIView commitAnimations]; 

This will be animated from the current frame of the frame (borders and position) to x = 45.0, y = 45.0, w = 100.0, h = 100.0.

You also may not understand the difference between the animation and the layer. You add animation to layers, but adding animation to a layer does not automatically set the property that you are animating.

CALayers are model objects. They contain information about the layer that ultimately appears on the screen. You must set the layer property if you want this property to really have this value. If you just animate a property, it will only be a visual representation, not an actual one - that is why this value returns to the original value of the layer, because you never changed it.

This leads me to the next point. You said:

Use "animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;" to ensure that values ​​are not reset at the end of the animation

This is not entirely correct. These two values ​​simply make the animation stay in the final position visually , however the actual values ​​of the layer have not changed. They are still the same values ​​that were when starting the animation. In the general case (with some exceptions) you do not want to use these two parameters, because they are only for visualization . What you want is to set the level value for the property that you are animating.

Say, for example, that you want to animate the position of your layer using explicit animation. Here is the code you want:

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; [animation setFromValue:[NSValue valueWithCGPoint:CGPointMake(70.0f, 70.0f)]]; [animation setToValue:[NSValue valueWithCGPoint:CGPointMake(150.0f, 150.0f)]]; [animation setDuration:2.0f]; // Actually set the position on the *layer* that you want it to be // when the animation finishes. [animationLayer setPosition:CGPointMake(150.0f, 150.0f)]; // Add the animation for the position key to ensure that you // override the animation for position changes with your animation. [animationLayer addAnimation:animation forKey:@"position"]; 

You can also consider animating animations. With an animation group, you can group several animations together, and then control how they relate to each other. In your case, the duration of your estimates and position animation is the same, and therefore, what you are trying to do works fine without a group, but if you want to compensate for the beginning of the animation, for example, you do not want the position of the animation to start before the second or two in frame animation, you can shake them by setting the beginTime value of the animation position.

Finally, I would be interested to know why you cannot use the implicit animations available in UIView. I use them in the vast majority of Core Animation code that I write, and I can’t understand why this will not work for your situation.

Sincerely.

+26
source share

The key path should only be the path to the property key, not the name of the object.

Use this

 [CABasicAnimation animationWithKeyPath:@"frame"] 

instead of this

 [CABasicAnimation animationWithKeyPath:@"layerImage.frame"] 

And just BTW, when you add an animation to a layer, the key does not mean the key property for the animation. Just the key (name) that is required for this animation (this refers to the last line of your code)

+3
source share

So, the solution was to animate the @ "borders" and @ "position" of the layer because the frame is not animated on the iPhone. It took me a while to realize that the position was the center of the layer and the size of the borders extended from the center, but that was the easy part.

So what I did in the summary:

  • In setFrame, create 2 animations with the bounds and position property.
  • Use "animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards;" so that the values ​​are not reset at the end of the animation.
  • Register a delegate for yourself to implement "animationDidStop: finished:". It seems that you still need to set the values: "layerImage.bounds = [animation.toValue CGRectValue]; layerImage.position = [animation.toValue CGPointValue];".

I was not able to use the UIView animation system directly because it did not do what I wanted on the layers.

Thanks to tadej5553 for pointing me to the layer problem that I had with "addAnimation". So, here is the code for those who would like to see how it looks.

 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { CABasicAnimation *animation = (CABasicAnimation*)anim; if ([animation.keyPath isEqualToString:@"bounds"]) { layerImage.bounds = [animation.toValue CGRectValue]; } else if ([animation.keyPath isEqualToString:@"position"]) { layerImage.position = [animation.toValue CGPointValue]; } } - (void)setFrame:(CGRect)value { CGRect bounds = CGRectMake(0, 0, value.size.width, value.size.height); if ([UIView isAnimationStarted]) { // animate the bounds CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"]; animation.duration = [UIView animationDuration]; animation.fromValue = [NSValue valueWithCGRect:layerImage.bounds]; animation.toValue = [NSValue valueWithCGRect:bounds]; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.timingFunction = [UIView animationFunction]; animation.delegate = self; [layerImage addAnimation:animation forKey:@"BoundsAnimation"]; // animate the position so it stays at 0, 0 of the frame. animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.duration = [UIView animationDuration]; animation.fromValue = [NSValue valueWithCGPoint:layerImage.position]; animation.toValue = [NSValue valueWithCGPoint:CGPointMake(bounds.size.width / 2, bounds.size.height / 2)]; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.timingFunction = [UIView animationFunction]; animation.delegate = self; [layerImage addAnimation:animation forKey:@"PositionAnimation"]; } else { layerImage.frame = bounds; } [super setFrame:value]; } 
+1
source share

All Articles