Loop animation - doesn't go - ios

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.

Related

Flickering in simple animation chain

I'm trying to animate a pulsting circle. But after the downward scale, there's something of a flickering effect. Any ideas to why that is? Here's my code so far:
- (instancetype)init
{
self = [super init];
if (self) {
if (!self.path) {
self.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:7 startAngle:0.0*(M_PI/180.0) endAngle:360.0*(M_PI/180.0) clockwise:YES].CGPath;
}
[self animateCircleUpward];
}
return self;
}
-(void)animateCircleUpward {
[CATransaction begin];
[CATransaction setCompletionBlock:^{[self animateCircleDownward];}];
CABasicAnimation * scaleUpwardAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy"];
scaleUpwardAnimation.fromValue = #(0.7);
scaleUpwardAnimation.toValue = #(1.0);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = #(1.0);
opacityAnimation.toValue = #(0.6);
self.fillColor = [UIColor greenColor].CGColor;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
NSArray *animations = #[scaleUpwardAnimation, opacityAnimation];
animationGroup.duration = 0.4;
animationGroup.animations = animations;
[self addAnimation:animationGroup forKey:#"pulse"];
[CATransaction commit];
}
-(void)animateCircleDownward {
[CATransaction begin];
[CATransaction setCompletionBlock:^{[self animateCircleUpward];}];
CABasicAnimation * scaleDownwardAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy"];
scaleDownwardAnimation.fromValue = #(1.0);
scaleDownwardAnimation.toValue = #(0.7);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = #(0.6);
opacityAnimation.toValue = #(1.0);
self.fillColor = [UIColor greenColor].CGColor;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
NSArray *animations = #[scaleDownwardAnimation, opacityAnimation];
animationGroup.duration = 0.4;
animationGroup.animations = animations;
[self addAnimation:animationGroup forKey:#"pulse"];
[CATransaction commit];
}
#end
Thanx in advance!
In regards to your actual question you likely need to set your CAAnimationGroup's fillMode property to kCAFillModeForward. The default value is kCAFillModeRemoved, so you are likely glimpsing the non-presentation layer between calls back and forth from your Down and Up methods.
That said, it appears that what you're trying to create is a cyclic animation through back and forth calls between your Up and Down methods via CATransaction's completionBlock. This seems an incredibly inefficient approach; why not try something more like:
-(void)scaleAndOpacityAnimations
{
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.xy”];
scaleAnimation.fromValue = #(0.7f);
scaleAnimation.toValue = #(1.0f);
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#“opacity”];
opacityAnimation.fromValue = #(1.0f);
opacityAnimation.toValue = #(0.6f);
CAAnimationGroup *scaleAndOpacity = [CAAnimationGroup animation];
scaleAndOpacity.animations = #[scaleAnimation, opacityAnimation];
scaleAndOpacity.autoReverses = YES;
scaleAndOpacity.repeatCount = HUGE_VALF;
//OP had no value indicated for the duration in posted code, I’m including it here for completion
scaleAndOpacity.duration = 0.5f;
[self addAnimation:scaleAndOpacity forKey:#“pulse”];
}
Which will simply repeat infinitely many times without having to instantiate new CAAnimations on each cycle.

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.

Two animations on one layer

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"];

Synchronising image, text, and positioning with CoreAnimation

I am a bit of a beginner with animations and have been experimenting with CoreAnimation for a couple of days. Feel free to warn me if this question does not make sense, but I'm trying to achieve the following. I have three objects:
one should be an image, moving according to a given pattern
one should be an UIImage that swaps two images
one should be a text (CATextLayer?) whose content changes
The three actions should happen in sync.
As an example, think about a programme showing a sinusoid function, like in a ECG, oscillating between -1 and +1: the first image would then move according to the current value (-1, -0.9, -0.8, ... 0, +0.1, +0.2, ... 1), the swap image would show "+" for positive values and "-" for negative values, and the text would alternate between "Positive" and "Negative".
I've tried with CAAnimationGroup but I'm clearly missing something. Some code:
{
// image that moves
CALayer *img1 = [CALayer layer];
img1.bounds = CGRectMake(0, 0, 20, 20);
img1.position = CGPointMake(x1,y1);
UIImage *img1Image = [UIImage imageNamed:#"image.png"];
img1.contents = (id)img1Image.CGImage;
// image that changes
CALayer *swap = [CALayer layer];
swap.bounds = CGRectMake(0, 0, 30, 30);
swap.position = CGPointMake(x2,y2);
NSString* nameswap = #"img_swap_1.png"
UIImage *swapImg = [UIImage imageNamed:nameswap];
// text
CATextLayer *text = [CATextLayer layer];
text.bounds = CGRectMake(0, 0, 100, 100);
text.position = CGPointMake(x3,y3);
text.string = #"Text";
// create animations
CGFloat duration = 0.2;
CGFloat totalDuration = 0.0;
CGFloat start = 0;
NSMutableArray* animarray = [[NSMutableArray alloc] init];
NSMutableArray* swapanimarray = [[NSMutableArray alloc] init];
NSMutableArray* textanimarray = [[NSMutableArray alloc] init];
float prev_x = 0;
float prev_y = 0;
// I get my values for moving the object
for (NSDictionary* event in self.events) {
float actual_x = [[event valueForKey:#"x"] floatValue];
float actual_y = [[event valueForKey:#"y"] floatValue];
// image move animation
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
CGPoint startPt = CGPointMake(prev_x,prev_y);
CGPoint endPt = CGPointMake(actual_x, actual_y);
anim.duration = duration;
anim.fromValue = [NSValue valueWithCGPoint:startPt];
anim.toValue = [NSValue valueWithCGPoint:endPt];
anim.beginTime = start;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[animarray addObject:anim];
// image swap animation
CABasicAnimation *swapanim = [CABasicAnimation animationWithKeyPath:#"contents"];
swapanim.duration = duration;
swapanim.beginTime = start;
NSString* swapnamefrom = [NSString stringWithFormat:#"%#.png", prev_name];
NSString* swapnameto = [NSString stringWithFormat:#"%#.png", current_name];
UIImage *swapFromImage = [UIImage imageNamed:swapnamefrom];
UIImage *swapToImage = [UIImage imageNamed:swapnameto];
swapanim.fromValue = (id)(swapFromImage.CGImage);
swapanim.toValue = (id)(swapToImage.CGImage);
swapanim.fillMode = kCAFillModeForwards;
swapanim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[swapanimarray addObject:swapanim];
//text animation
CABasicAnimation *textanim = [CABasicAnimation animationWithKeyPath:#"contents"];
textanim.duration = duration;
textanim.fromValue = #"Hey";
textanim.toValue = #"Hello";
textanim.beginTime = start;
[textanimarray addObject:textanim];
// final time settings
prev_x = actual_x;
prev_y = actual_y;
start = start + duration;
totalDuration = start + duration;
}
}
CAAnimationGroup* group = [CAAnimationGroup animation];
[group setDuration:totalDuration];
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;
[group setAnimations:animarray];
CAAnimationGroup* swapgroup = [CAAnimationGroup animation];
[swapgroup setDuration:totalDuration];
swapgroup.removedOnCompletion = NO;
swapgroup.fillMode = kCAFillModeForwards;
[swapgroup setAnimations:swapanimarray];
CAAnimationGroup* textgroup = [CAAnimationGroup animation];
[textgroup setDuration:totalDuration];
textgroup.removedOnCompletion = NO;
textgroup.fillMode = kCAFillModeForwards;
[textgroup setAnimations:textanimarray];
[ball addAnimation:group forKey:#"position"];
[swap addAnimation:flaggroup forKey:#"position"];
[text addAnimation:textgroup forKey:#"contents"];
[self.layer addSublayer:ball];
[self.layer addSublayer:swap];
[self.layer addSublayer:text];
}
Now... the problems:
1) the swap image reverts to the original at every swap. So, if I swap A->B, I see it going from A to B in the expected duration time, but then reverting to A. I've read a number of threads on SO about this but couldn't get it to work.
2) changing the string of the text layer in a timed fashion... is this possible with this infrastructure? Basically, I'm trying to get the text and the swap image to change as soon as the first image moves, as described in the example.
3) setting the delegate for the CABasicAnimation doesn't have any effect, although it does for the CAAnimationGroup: as a result, you can't manage events like animationDidStop for every single animation, just for the whole group. Is there any alternative way to do so?
4) following from 3), is it possible, using CAAnimationGroup, to intercept the events to create a stop/start behaviour? Let's suppose I wanted to have play/stop buttons, and to resume the animation from exactly where I had left it?
As a conclusive question, I would simply like to know if anyone did something similar and, most importantly, if this way of doing things (using a CAAnimationGroup) is actually the way to go or if it's better to use CAKeyFrameAnimation or something else.
I managed to solve the problem, albeit with some workarounds.
1) The trick here is that
removedOnCompletion = NO;
fillMode = kCAFillModeForwards;
need to be assigned to every single animation, not to the CAAnimationGroup.
2) This requires a separate animation to be determined for the CATextLayer, and animating the "string" property. In this case, the code was right except for the "contents" key-value which should have been
[text addAnimation:textgroup forKey:#"contentAnimate"];
Alternatively, see point 3. The callback way of operation allows to change a label.text.
3) The only way to do this is to actually not use the CAAnimationGroup and set up a sequence of CABasicAnimation. The general schema is: create the first CABasicAnimation in a function, assign the delegate. In the animationDidStop method, add a callback to the function that creates the CABasicAnimation. This way, each animation will be created. At the same time, this allows to intercept and react to specific events within the animation.
-(void)performNextAnimation:(int)index {
// ... this gives for granted you have an object for #index ...
NSDictionary* thisEvent = [self.events objectAtIndex:index];
float prev_x = [thisEvent valueForKey:#"prev_x"];
float prev_y = [thisEvent valueForKey:#"prev_x"];
float actual_x = [thisEvent valueForKey:#"prev_x"];
float actual_y = [thisEvent valueForKey:#"prev_x"];
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
GPoint startPt = CGPointMake(prev_x,prev_y);
CGPoint endPt = CGPointMake(actual_x, actual_y);
anim.duration = 0.5;
anim.fromValue = [NSValue valueWithCGPoint:startPt];
anim.toValue = [NSValue valueWithCGPoint:endPt];
anim.beginTime = start;
[anim setDelegate:self];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[object addAnimation:anim forKey:#"position"];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
// ... your conditions go here ...
// ... your actions go here ...
// e.g. set label.text
[self performNextAnimation];
}
4) Following from 3, this is not possible in CAAnimationGroup. Reading the docs, I would say that CAAnimationGroup is not intended to be used for sequences in which each event represents a step forward in time (for this you need to use CAKeyFrameAnimation or a sequence of CABasicAnimation with a callback function, as I did). CAAnimationGroup is intended for linked animation that should be executed on an all-or-nothing basis (similarly to CATransaction).

Resources