Two animations on one layer - ios

I have a CALayer, and I want to show it and then hide.
CALayer *layerOne = [CALayer layer];
[layerOne addSublayer:textOne];
layerOne.frame = CGRectMake(0, 0, size.width, size.height);
[layerOne setMasksToBounds:YES];
layerOne.opacity = 0.0;
CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath:#"opacity"];
[animationOne setDuration:0];
[animationOne setFromValue:[NSNumber numberWithFloat:0.0]];
[animationOne setToValue:[NSNumber numberWithFloat:1.0]];
[animationOne setBeginTime:3];
[animationOne setRemovedOnCompletion:NO];
[animationOne setFillMode:kCAFillModeForwards];
[layerOne addAnimation:animationOne forKey:#"animateOpacity"];
This code work successfully, layerOne appear after 3 seconds.
But I want to hide this layer, so I add this:
CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath:#"opacity"];
[animationTwo setDuration:0];
[animationTwo setFromValue:[NSNumber numberWithFloat:1.0]];
[animationTwo setToValue:[NSNumber numberWithFloat:0.0]];
[animationTwo setBeginTime:6];
[animationTwo setRemovedOnCompletion:NO];
[animationTwo setFillMode:kCAFillModeForwards];
[layerOne addAnimation:animationTwo forKey:#"animateOpacity"];
And it doesn't work. layerOne not appear after 3 seconds. Its just flashed in second 6 and disappear. Its seems like the second animation blocking the first one and only the second animation is going on.
What I do wrong?

Well, for one thing since the second animation has the same key, when you add it to the layer, the original animation will be removed. When the animation is removed, it's long-term effect (setting opacity = 1.0) will also be removed so the animation will be immediately hidden.
For something like this, the normal process to show the layer is:
// set the final result you want to persist forever
layerOne.opacity = 1.0;
// set up your animation here
CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath:#"opacity"];
animationOne.fromValue = #(0.);
animationOne.toValue = #(1.);
animationOne.duration = 3.;
animationOne.beginTime = 0.;
animationOne.removedOnCompletion = true;
animationOne.fillMode = kCAFillModeRemove; // for clarity, this is the default
[layerOne addAnimation:animationOne forKey:#"animateOpacity"];
And then when you want to hide the layer just reverse the process:
// Set the final animation state
layerOne.opacity = 0.0;
// set up your animation here
CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath:#"opacity"];
animationTwo.fromValue = #(1.);
animationTwo.toValue = #(0.);
animationTwo.duration = 3.;
animationTwo.beginTime = 0.;
animationTwo.removedOnCompletion = true;
animationTwo.fillMode = kCAFillModeRemove; // for clarity, this is the default
[layerOne addAnimation:animationTwo forKey:#"animateOpacity"];
If you're wanting to run the whole process as a single event, you should put both animations into a single animation group:
// set up your animation here
CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath:#"opacity"];
animationOne.fromValue = #(0.);
animationOne.toValue = #(1.);
animationOne.duration = 3.;
animationOne.beginTime = 0.;
animationOne.fillMode = kCAFillModeForwards;
// set up your animation here
CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath:#"opacity"];
animationTwo.fromValue = #(1.);
animationTwo.toValue = #(0.);
animationTwo.beginTime = 6.;
animationTwo.duration = 3.;
animationOne.fillMode = kCAFillModeForwards;
// set up the animation group
CAAnimationGroup* group = [CAAnimationGroup new];
group.beginTime = 0.;
group.duration = 9.;
group.animations = #[ animationOne, animationTwo ];
[layerOne addAnimation:group forKey:#"animateOpacity"];

Related

CATextLayer foregroundColor animation don't work on iOS9 but work on iOS10 or high

CATextLayer foregroundColor animation don't work on iOS9 but work normal on iOS10 or high version! I don't know what's wrong with my code!
Code as follow:
- (void)addAnimationOnLayer:(CATextLayer*)layer{
CABasicAnimation *colorAn = [CABasicAnimation animationWithKeyPath:#"foregroundColor"];
colorAn.fromValue = (id)NeColor333333.CGColor;
colorAn.toValue = (id)NeColor999999.CGColor;
CABasicAnimation *sizeAn = [CABasicAnimation animationWithKeyPath:#"fontSize"];
sizeAn.fromValue = #16.0;
sizeAn.toValue = #12.0;
CAKeyframeAnimation *keyAn = [CAKeyframeAnimation animationWithKeyPath:#"position"];
keyAn.values = #[[NSValue valueWithCGPoint:layer.position],[NSValue valueWithCGPoint:CGPointMake(layer.position.x, layer.position.y - 16)]];
keyAn.keyTimes =#[#0.0,#1.0];
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = #[colorAn, keyAn, sizeAn];
group.duration = 0.4;
group.repeatCount = 1;
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[layer addAnimation:group forKey:nil];
}
You can add a CALayer, and set CALayer mask is a CATextLayer, you an change CALayer backgroundColor with animation.

make animation continues on multi layers by using CAAnimation

I'm creating a tutorial screen to let user understand what to do in the screen.
For example, I have viewPlug and viewBulb. First, viewPlug moves up until it collide viewBulb. After that viewBulb changes color to green to notify. After some seconds, viewPlug moves down and viewBulb changes to red immediately.
So far, I'm digging in UIViewAnimation and UIViewKeyframeAnimation but none of them give me full right to handle complex animation, duration, beginTime... Only CAAnimation can do exactly what I want in scenarios.
But I'm not sure if this is a best way to do in my case, or iOS supports other methods that easier/simpler to handle?
Here is my sample code.
- (void)makeAnimations {
// Group all animation into 1 transaction
[CATransaction begin];
// Create plug animation
CABasicAnimation *animatePlug = [CABasicAnimation animationWithKeyPath:#"position.y"];
animatePlug.beginTime = 0;
animatePlug.byValue = #(-180);
animatePlug.duration = 1.5;
animatePlug.fillMode =kCAFillModeForwards;
animatePlug.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Create unplug animation
CABasicAnimation *animateUnplug = [CABasicAnimation animationWithKeyPath:#"position.y"];
animateUnplug.beginTime = 4;
animateUnplug.byValue = #100;
animateUnplug.duration = 0.25;
animateUnplug.fillMode =kCAFillModeForwards;
animateUnplug.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Group all animation of view A
CAAnimationGroup *animateGroupPosition = [CAAnimationGroup animation];
animateGroupPosition.duration = 6;
animateGroupPosition.animations = #[animatePlug, animateUnplug];
animateGroupPosition.repeatCount = HUGE_VALF;
[self.viewPlug.layer addAnimation:animateGroupPosition forKey:#"position"];
// Create plug color animation
CABasicAnimation *animateGreen = [CABasicAnimation animationWithKeyPath:#"backgroundColor"];
animateGreen.toValue = (__bridge id)([UIColor greenColor].CGColor);
animateGreen.beginTime = 1.5;
animateGreen.duration = 0.5;
animateGreen.fillMode =kCAFillModeForwards;
// Create unplug color animation
CABasicAnimation *animateRed = [CABasicAnimation animationWithKeyPath:#"backgroundColor"];
animateRed.toValue = (__bridge id)([UIColor redColor].CGColor);
animateRed.beginTime = 4;
animateRed.duration = 0.1;
animateRed.fillMode =kCAFillModeForwards;
// Group all animation of view B
CAAnimationGroup *animateGroupColor = [CAAnimationGroup animation];
animateGroupColor.duration = 6;
animateGroupColor.animations = #[animateGreen, animateRed];
animateGroupColor.repeatCount = HUGE_VALF;
[self.viewBulb.layer addAnimation:animateGroupColor forKey:#"color"];
[CATransaction commit];
}

Rotating UIImageView around anchorpoint issues

I am attempting to rotate a UIImageView around an anchor point to simulate a "radar sweep" style animation. My understanding of anchor points leads me to believe that (1, 1) should be the bottom right corner of the radar image. However, setting the ImageView's anchor point to this doesn't give me the behavior I anticipated. This can be viewed here.
As can be seen, the anchor point is a ways up the right side of the image, though it is correctly at the center. What I want is the image to rotate around the center point, like a radar sweep.
Here is the relevant code:
self.radarView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, barDimension, barDimension)];
self.radarView.contentMode = UIViewContentModeScaleAspectFill;
self.radarView.image = [UIImage imageNamed:#"radarSweep.png"];
self.radarView.layer.anchorPoint = CGPointMake(1, 1);
self.radarView.layer.position = CGPointMake(screenWidth / 2.0, screenHeight / 2.0);
[self.mapView addSubview:self.radarView];
CABasicAnimation *opacityInAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
[opacityInAnim setFromValue:[NSNumber numberWithFloat:0.0]];
[opacityInAnim setToValue:[NSNumber numberWithFloat:1.0]];
opacityInAnim.duration = 0.25;
opacityInAnim.fillMode = kCAFillModeForwards;
opacityInAnim.removedOnCompletion = NO;
opacityInAnim.beginTime = 0.0;
opacityInAnim.delegate = self;
[opacityInAnim setValue:#"radarSweep" forKey:#"animName"];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0];
animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
animation.duration = 1.0f;
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = 2;
animation.removedOnCompletion = NO;
[animation setBeginTime:0.0];
animation.delegate = self;
[animation setValue:#"radarSweep" forKey:#"animName"];
animation.removedOnCompletion = YES;
CABasicAnimation *opacityOutAnimBar = [CABasicAnimation animationWithKeyPath:#"opacity"];
[opacityOutAnimBar setFromValue:[NSNumber numberWithFloat:1.0f]];
[opacityOutAnimBar setToValue:[NSNumber numberWithFloat:0.0f]];
opacityOutAnimBar.delegate = self;
opacityOutAnimBar.fillMode = kCAFillModeForwards;
opacityOutAnimBar.removedOnCompletion = NO;
[opacityOutAnimBar setDuration:0.25];
[opacityOutAnimBar setValue:#"radarSweep" forKey:#"animName"];
[opacityOutAnimBar setBeginTime:1.35];
CAAnimationGroup *group = [CAAnimationGroup animation];
[group setDuration:2.0];
group.delegate = self;
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
[group setAnimations:#[opacityInAnim, animation, opacityOutAnimBar]];
[group setValue:#"radarSweep" forKey:#"animName"];
[self.radarView.layer addAnimation:group forKey:#"group"];
Any advice would be greatly appreciated. Thanks in advance.
Your radar image isn't square but the imageview is, so the image is bleeding over the bounds of the imageView. You can either do self.radarView.layer.clipsToBounds = YES or change the content mode to ScaleToFit. Or you can resize your image view to match the proportions of the radar image.

Loop animation - doesn't go

I'm trying to repeat a sequence of animations, inside a LOOP, changing at each loop some parameters randomly. Here is the code. Anyone please knows why it doesn't work?
If I call it once with a button action, it works, but with a loop it doesn't.
Thanks a lot! Giuseppe
-(IBAction)startLoop:(id)sender {
for (int i=1;i<10; i++) {
[self animation2];
}
}
-(id) animation2 {
int max=500;
UIImage *myImage = [UIImage imageNamed:#"coccinella2.png"];
CALayer *myLayer = [CALayer layer];
myLayer.contents = (id)myImage.CGImage;
myLayer.bounds = CGRectMake(0, 0, 50, 60);
[myLayer setPosition:CGPointMake(arc4random()%(max), arc4random()%(max))];
[myLayer setBounds:CGRectMake(0.0, 0.0, 50.0, 60.0)];
[self.view.layer addSublayer:myLayer];
//translation1
CGPoint startPt = CGPointMake(arc4random()%(max),arc4random()%(max));
CGPoint endPt = CGPointMake(arc4random()%(max),arc4random()%(max));
CABasicAnimation *transl1 = [CABasicAnimation animationWithKeyPath:#"position"];
transl1.removedOnCompletion = FALSE;
transl1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
transl1.fromValue = [NSValue valueWithCGPoint:startPt];
transl1.toValue = [NSValue valueWithCGPoint:endPt];
transl1.duration = 2.0;
transl1.fillMode = kCAFillModeForwards;
transl1.beginTime = 0;
//scale 1
CABasicAnimation *scale1 = [CABasicAnimation
animationWithKeyPath:#"transform.scale"];
scale1.removedOnCompletion = FALSE;
[scale1 setToValue:[NSNumber numberWithInt:3]];
[scale1 setDuration:2.0f];
scale1.fillMode = kCAFillModeForwards;
scale1.beginTime = 0;
//rotation1
CABasicAnimation *rotation1 = [CABasicAnimation
animationWithKeyPath:#"transform.rotation.z"];
rotation1.removedOnCompletion = FALSE;
[rotation1 setFromValue:DegreesToNumber(0)];
[rotation1 setToValue:DegreesToNumber(90)];
//rotation1.repeatCount = HUGE_VALF;
[rotation1 setDuration:2.0f];
rotation1.fillMode = kCAFillModeForwards;
rotation1.beginTime = 0;
//group
CAAnimationGroup* group = [CAAnimationGroup animation];
[group setDuration: 6.0];
group.removedOnCompletion = FALSE;
group.fillMode = kCAFillModeForwards;
[group setAnimations: [NSArray arrayWithObjects:scale1, transl1, rotation1, nil]];
[myLayer addAnimation: group forKey: nil];
}
Your code doesn't repeat the annotation 10 times but starts 10 animations right away. If your goal is to start the animations after the previous one ended you should try using an NSTimer.
You can use an NSTimer to trigger each group of animations at a different time, or you can set the beginTime on each group, with code like this:
group.beginTime = CACurrentMediaTime() + delay;
For animations, using the beginTime gives more accurate timing, since CA is run on a separate thread and doesn't stall like NSTimers if your app gets busy.

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