How to animate when switching CALayers - ios

I'm adding and removing CALayers using this method :
[[[self.view.layer sublayers] objectAtIndex:0] removeFromSuperlayer];
if(++self.backgroundIndex > self.gradients.count - 1) {
self.backgroundIndex = 0;
}
CAGradientLayer *layer = [self.gradients objectAtIndex:self.backgroundIndex];
layer.frame = self.view.frame;
[self.view.layer insertSublayer:layer atIndex:0];
How can I animate this ? Thanks.

You can use Core Animation when adding and removing CALayers something like this
CABasicAnimation *fadeOut = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeOut.fromValue = [NSNumber numberWithFloat:1.0];
fadeOut.toValue = [NSNumber numberWithFloat:0.0];
fadeOut.duration = 1.0; // 1 second
[[[self.view.layer sublayers] objectAtIndex:0] addAnimation:fadeOut forKey:#"fadeOutAnimation"];
//Adding Layer animation
CAGradientLayer *layer = [self.gradients objectAtIndex:self.backgroundIndex];
CABasicAnimation *fadeIn = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeIn.fromValue = [NSNumber numberWithFloat:1.0];
fadeIn.toValue = [NSNumber numberWithFloat:0.0];
fadeIn.duration = 1.0; // 1 second
[layer addAnimation:fadeIn forKey:#"fadeInAnimation"];

Related

Animation resets to begin state after ending or firing second animation

I'm trying to make a breathing circle animation. Only breathInAnimation (grow circle) seems to animate. breathOutAnimation (shrink circle) gets called but doesn't seem to do anything. I'm guessing it immediately reverts back to the starting state but I don't understand why.
- (void)viewDidLoad
animationView = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0, 200, 200)];
animationView.backgroundColor = [UIColor blueColor];
animationView.layer.cornerRadius = 100;
animationView.center = self.view.center;
[self.view addSubview: animationView];
[self drawCircleEdge];
[self breathInAnimation];
[NSTimer timerWithTimeInterval:7.0 target:self selector:#selector(breathOutAnimation) userInfo:nil repeats:YES];
- (void)breathInAnimation
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
[scaleAnimation setValue:#"breathIn" forKey:#"id"];
scaleAnimation.duration = 4;
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.1];
scaleAnimation.toValue = [NSNumber numberWithFloat:1];
[animationView.layer addAnimation:scaleAnimation forKey:#"scale"];
- (void)breathOutAnimation
CABasicAnimation *breathOut = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
breathOut.duration = 8;
breathOut.fromValue = [NSNumber numberWithFloat:1];
breathOut.toValue = [NSNumber numberWithFloat:0.1];
[animationView.layer removeAllAnimations];
[animationView.layer addAnimation: breathOut forKey:#"scale"];
I also tried using the delegate of scaleAnimation.
- (void)animationDidStop:(CABasicAnimation *)theAnimation2 finished:(BOOL)flag
This works but after the animation finished the circle goes back to the state it was after ending the first animation. Shrinking -> animation ends -> fullsize again.
I'm not sure what I'm missing here.
CAAnimations doesn't apply the transformation to your layer, it's animating on a presentation layer and then switch back to your layer when animation is finished.
You should apply your transform when you are playing the animation.
-(void)breathOutAnimation
{
CABasicAnimation *breathOut = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
breathOut.duration = 8;
breathOut.fromValue = [NSNumber numberWithFloat:1];
breathOut.toValue = [NSNumber numberWithFloat:0.1];
[animationView.layer removeAllAnimations];
[animationView.layer addAnimation: breathOut forKey:#"scale"];
animationView.layer.transform = CATransform3DMakeScale(0.1, 0.1, 0.1);
}

Add a bounce effect to a flip animation

I animated a flip for a pie chart I have. Here is my code"
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:0.5];
scaleAnimation.duration = 1.0f;
scaleAnimation.removedOnCompletion = NO;
[self.pieChart addAnimation:scaleAnimation forKey:#"scale"];
animationDidStop:finished:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
animation.fromValue = [NSNumber numberWithFloat:0.5];
animation.toValue = [NSNumber numberWithFloat:1.0];
animation.duration = 1.0f;
[self.pieChart addAnimation:animation forKey:#"scale"];
How can I add a bounce effect to the second animation? (The animation in animationDidStop:finished:)
Go with a CAKeyFrameAnimation instead of two CABasicAnimation:
CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.x"];
scaleAnimation.keyTimes = #[#(0), #(0.5), #(0.9), #(1)];
scaleAnimation.values = #[#(1.0), #(0.5), #(1.1), #(1.0)];
scaleAnimation.duration = 1.0f;
scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
With keyTimes and values, you can specify multiple stances in your animation. Here, I made it start with a scale of 1, animating to a scale of 0.5 (your first animation values), then 1.1 (an overshoot to create a "bounce") and finally 1 (your second animation values). Tweak those to your liking!
If you want to do this animation in two parts, you can keep your first animation, then start a CAKeyFrameAnimation in the animationDidStop:finished: similar to this instead:
CAKeyframeAnimation *scaleAnimation = [CAKeyframeAnimation animationWithKeyPath:#"transform.scale.x"];
scaleAnimation.keyTimes = #[#(0), #(0.8), #(1)];
scaleAnimation.values = #[#(0.5), #(1.1), #(1.0)];
scaleAnimation.duration = 1.0f;
scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
If you instead want to use UIView animation blocks, you'll need to do something similar to this:
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.squareView.transform = CGAffineTransformMakeScale(0.5, 1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:1 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:0 animations:^{
self.squareView.transform = CGAffineTransformIdentity;
} completion:nil];
}];
You will probably want to play with the params to tweak the animation.

Animation is not working as expected

I implemented corePlot in xcode and I'm using the pie chart. I'm trying to create a 3d flip animation while the chart reloads. Here is the code:
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:0.5];
scaleAnimation.duration = 1.0f;
scaleAnimation.removedOnCompletion = NO;
[self.pieChart addAnimation:scaleAnimation forKey:#"scale"];
[self.pieChart reloadData];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
animation.fromValue = [NSNumber numberWithFloat:0.5];
animation.toValue = [NSNumber numberWithFloat:1.0];
animation.duration = 1.0f;
[self.pieChart addAnimation:animation forKey:#"scale"];
I didn't get desirable effects. I think what's happening is, both animations are happening at once. (Though I'm not sure that is what's happening.)
Also, is it possible to add z-depth? If so, how?
Update
I tried the follwoing:
CABasicAnimation *currentAnition = (CABasicAnimation *)anim;
if (currentAnition == self.scaleAnimation {...}
And it didn't work.
CAAnimation is KVC compliant, so we can store whether you're starting or finishing for lookup in the delegate methods:
{
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
[scaleAnimation setValue:#(YES) forKey:#"scaleAnimation"];
// set the delegate
scaleAnimation.delegate = self;
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:0.5];
scaleAnimation.duration = 1.0f;
scaleAnimation.removedOnCompletion = NO;
[self.pieChart addAnimation:scaleAnimation forKey:#"scale"];
[self.pieChart reloadData];
}
- (void)runSecondAnimation
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
[animation setValue:#(YES) forKey:#"secondAnimation"];
animation.delegate = self;
animation.fromValue = [NSNumber numberWithFloat:0.5];
animation.toValue = [NSNumber numberWithFloat:1.0];
animation.duration = 1.0f;
[self.pieChart addAnimation:animation forKey:#"scale"];
}
/* Called when the animation either completes its active duration or
* is removed from the object it is attached to (i.e. the layer). 'flag'
* is true if the animation reached the end of its active duration
* without being removed. */
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
BOOL scaleAnimation = [[anim valueForKey:#"scaleAnimation"] boolValue];
if (scaleAnimation) {
[self runSecondAnimation];
}
BOOL secondAnimation = [[anim valueForKey:#"secondAnimation"] boolValue];
if (secondAnimation) {
[self runThirdAnimation];
}
}
And remember you have also:
/* Called when the animation begins its active duration. */
- (void)animationDidStart:(CAAnimation *)anim;

CABasicAnimation of object around itself (360 degrees)

Here's my Animation code:
CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotation.duration = 4;
fullRotation.repeatCount= 1000;
[[stick layer] addAnimation:fullRotation forKey:#"60"];
fullRotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
I'm trying to rotate "stick" around itself , but it seems that it's rotating around another point , which is by default the origin. What can I do to make it accomplish a full 360 degrees rotation around itself? Thanks in advance.
If you want to change the anchor point of stick use:
stick.layer.anchorPoint = CGPointMake(1.0,1.0);
https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CALayer_class/Introduction/Introduction.html#//apple_ref/occ/instp/CALayer/anchorPoint
The rotation code I use:
CABasicAnimation *rotateAnim = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotateAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
rotateAnim.fromValue = [NSNumber numberWithFloat:0];
rotateAnim.toValue = [NSNumber numberWithFloat:(360 * M_PI / 180.0f)];
rotateAnim.repeatCount = HUGE_VALF;
rotateAnim.duration = 2.5;
rotateAnim.removedOnCompletion = NO;
[view.layer addAnimation:rotateAnim forKey:#"rotationAnimation"];

Animate UISegmentedControl image

Im trying to animate an image in a UISegmentedControl. This is what I have come up with but it doesn't work.
The animation itself works fine if I switch imageView.layer to _segmentedControl.layer in the last line of code.
What am I missing?
UIImageView *imageView = [[UIImageView alloc] init];
imageView.image = [_segmentedControl imageForSegmentAtIndex:0];
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scale.duration = 0.25;
scale.repeatCount = 2;
scale.autoreverses = YES;
scale.fromValue = [NSNumber numberWithFloat:1.0];
scale.toValue = [NSNumber numberWithFloat:0.0];
[imageView.layer addAnimation:scale forKey:#"scale"];
For future reference I would like to share the solution I came up with. Instead of animating the image for the segment, which doesn't seem possible, you can at least animate the subview of the segmented control.
UIView *subView = [[_segmentedControl subviews] objectAtIndex:0];
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scale.duration = 0.25;
scale.repeatCount = 1;
scale.autoreverses = YES;
scale.fromValue = [NSNumber numberWithFloat:1.0];
scale.toValue = [NSNumber numberWithFloat:0.95];
[subView.layer addAnimation:scale forKey:#"scale"];
You use the collection UIView. UISegmentedControl can not animate

Resources