What's the meaning of progress keypath in CABasicAnimation? - ios

A code snippet from https://github.com/danielamitay/DACircularProgress/blob/master/DACircularProgress/DACircularProgressView.m
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"progress"];
animation.duration = duration;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fillMode = kCAFillModeForwards;
animation.fromValue = [NSNumber numberWithFloat:self.progress];
animation.toValue = [NSNumber numberWithFloat:pinnedProgress];
animation.beginTime = CACurrentMediaTime() + initialDelay;
animation.delegate = self;
[self.circularProgressLayer addAnimation:animation forKey:#"progress"];
But I could not find progress in the offical document. Does the keypath need to be animatable property? What does the progress mean?

From this answer (which is coincidentally animating progress too):
Firstly we need to create a new subclass of CALayer that has an animatable property called 'progress'.
Your snippet does indeed have a property called 'progress'
#property(nonatomic) CGFloat progress;
It appears the animation is animating that property

It is because the progress is an animatable property for this specific uiview. We could custom animatable property refer: Create a custom animatable property

Related

CALayer position animation on video layer

In this tutorial I have found how to make an animation CALayer in video:
http://www.raywenderlich.com/30200/avfoundation-tutorial-adding-overlays-and-animations-to-videos
CABasicAnimation *animation
=[CABasicAnimation animationWithKeyPath:#"opacity"];
animation.duration=3.0;
animation.repeatCount=5;
animation.autoreverses=YES;
// animate from fully visible to invisible
animation.fromValue=[NSNumber numberWithFloat:1.0];
animation.toValue=[NSNumber numberWithFloat:0.0];
animation.beginTime = AVCoreAnimationBeginTimeAtZero;
[overlayLayer1 addAnimation:animation forKey:#"animateOpacity"];
But it does not work when I want to animate the movement of CALayer:
animation.keyPath = #"position.x";
or
animation.keyPath = #"position.y";
Is it possible to animate the movement of the CALayer?
Any animations will be interpreted on the video's timeline, not real-time, so you should:
Set animations’ beginTime property to AVCoreAnimationBeginTimeAtZero rather than 0 (which CoreAnimation replaces with CACurrentMediaTime);
Set removedOnCompletion to NO on animations so they are not automatically removed;
Try using this keypath :
animation.keyPath = #"transform.translation.x";

Transition animation does not take place for every text change

I have added transition animation to UILabel for text change.
But it takes place only once when I add it to layer.
- (IBAction)changeText:(id)sender {
if (!self.transitionAnimation) {
CATransition *animation = [CATransition animation];
animation.duration = 0.5;
animation.type = kCATransitionFade;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.removedOnCompletion = NO;
[self.label.layer addAnimation:animation forKey:nil];
self.transitionAnimation = animation;
}
self.label.text = [NSString stringWithFormat:#"Text %d",arc4random()];
}
Why do you need to have a strong reference to the animation? That reference doesn't get nil'ed out, and hence your control doesn't enter the if block more than once.
Either nil out the transitionAnimation property on didStop, or don't store a reference at all.
Edit: Also, I don't think you need to have removedOnCompletion as NO.

How do I make a layer maintain the final value of a CABasicAnimation

Consider the following animation:
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 1.0;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
pathAnimation.removedOnCompletion = NO;
pathAnimation.delegate = self;
This will essentially animate the drawing of the layer from one end to the next. The problem is that once the animation completes, the strokeEnd property resets back to 0 (where it was initially set). How do I make the final value "stick"?
I have attempted to change this in the animationDidStop delegate method. This mostly works, but can cause a flash of strokeEnd at 0 briefly, even when put inside a CATransaction to disable animations. I have also played with the additive and cumulative properties to no avail. Any suggestions?
You simply set the strokeEnd property to its final value yourself! Like this:
// your current code (stripped of unnecessary stuff)
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 1.0;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
// just add this to it:
theLayer.strokeEnd = 1;
// and now add the animation to theLayer
If strokeEnd were implicitly animatable (so that that line would itself cause animation) we would turn off implicit animation first:
[CATransaction setDisableActions:YES];
theLayer.strokeEnd = 1;
Note that your pathAnimation.removedOnCompletion = NO; is wrong, and so is the other answer's kCAFillModeForwards. Both are based on misunderstandings of how Core Animation works.

how to use set layer value automatically when use explicit animation?

I can animately move a layer like this:
CABasicAnimation *animation = [CABasicAnimation animation];
[animation setFromValue:[NSValue valueWithCGPoint:self.layer.position]];
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(100, 100)]];
[self.layer addAnimation:animation forKey:#"position"];
self.layer.position = CGPointMake(100, 100);
however, I have to set the layer.position after the end of animation, is there a way to set it automatically when using explicit animation?
According to the Apple documentation, you have to set it manually :
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html#//apple_ref/doc/uid/TP40004514-CH3-SW3
CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 1.0;
[theLayer addAnimation:fadeAnim forKey:#"opacity"];
// Change the actual data value in the layer to the final value.
theLayer.opacity = 0.0;
Unlike an implicit animation, which updates the layer object’s data value, an explicit animation does not modify the data in the layer tree. Explicit animations only produce the animations. At the end of the animation, Core Animation removes the animation object from the layer and redraws the layer using its current data values. If you want the changes from an explicit animation to be permanent, you must also update the layer’s property as shown in the preceding example.

CAAnimationGroup reverts to original position on completion

In iOS I'm trying to create the effect of an icon shrinking in size, and flying across the screen in an arc while fading out, and then disappearing. I've achieved these 3 effects with an CAAnimationGroup, and it does what I want. The problem is when the animation ends, the view appears back at the original position, full size and full opacity. Can anyone see what I am doing wrong in the code below?
The animation should not revert to it's original position, but just disappear at the end.
UIBezierPath *movePath = [UIBezierPath bezierPath];
CGPoint libraryIconCenter = CGPointMake(610, 40);
CGPoint ctlPoint = CGPointMake(self.imgViewCropped.center.x, 22.0);
movePath moveToPoint:self.imgViewCropped.center];
[movePath addQuadCurveToPoint:libraryIconCenter
controlPoint:ctlPoint];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = NO;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
scaleAnim.removedOnCompletion = NO;
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"alpha"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.0];
opacityAnim.removedOnCompletion = NO;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim,scaleAnim,opacityAnim, nil];
animGroup.duration = 0.6;
animGroup.delegate = self;
animGroup.removedOnCompletion = NO;
[self.imgViewCropped.layer addAnimation:animGroup forKey:nil];
I believe you need to set the fillMode property of your animations to kCAFillModeForwards. That should freeze the animations at their end time. Another suggestion (and honestly, this is what I'd usually do) is just se the properties of the layer itself to their final position after you've set up the animation. That way when the animation is removed, the layer will still have the final properties as part of its model.
As an aside, the removedOnCompletion flag of animations contained within a CAAnimationGroup is ignored. You should probably just remove those assignments since they're misleading. Replace them with assignments to fillMode as specified above.

Resources