I have two animations for two layers, layer1 and layer2, each has different path. So, I want to know how can play them in order. first play layer1's animation, and then layer2.
here is my two animation code!
layer1
CALayer *layer1 = [CALayer layer];
[self.layer addSublayer:layer1];
CAKeyframeAnimation *animation1 = [CAKeyframeAnimation animationWithKeyPath:#"position"];
layer1.contents = (id)image1.CGImage;
layer1.anchorPoint = CGPointZero;
layer1.frame = CGRectMake(0.0f, 0.0f, image1.size.width, image1.size.height);
animation1.path = path1;
animation1.duration = 2.0f;
animation1.calculationMode = kCAAnimationLinear;
[layer1 addAnimation:animation1 forKey:#"position"];
layer2
CALayer *layer2 = [CALayer layer];
[self.layer addSublayer:layer2];
CAKeyframeAnimation *animation2 = [CAKeyframeAnimation animationWithKeyPath:#"position"];
layer2.contents = (id)image2.CGImage;
layer2.anchorPoint = CGPointZero;
layer2.frame = CGRectMake(0.0f, 0.0f, image2.size.width, image2.size.height);
animation2.path = path2;
animation2.duration = 2.0f;
animation2.calculationMode = kCAAnimationLinear;
[layer2 addAnimation:animation2 forKey:#"position"];
Set a delegate for the first animation and start the second animation in the animationDidStop:finished: delegate method.
Related
I am making an iPhone app that draws a circle on the screen. I use a timer (every 5 second), so that I can 'grow' the circle, then wait 5 seconds then grow again. Now I am having trouble when animating the increase in size of the circle (I use paths and a CAShapeLayer layer). The size (from and to) are fine with the animation, its just when it starts the circle moves to the upper left hand side and grows from there. Any help or suggestions would be great. Thanks
The class where this is implemented is a UIControl, which is added to a UIView.
//Called in init method
(void) initalPop {
//Circle
self.bubble = [CAShapeLayer layer];
self.bubble.bounds = self.bounds;
self.bubble.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
self.bubble.fillColor = [
[UIColor greenColor] CGColor
];
self.bubble.strokeColor = [
[UIColor greenColor] CGColor
];
self.bubble.lineWidth = 4.0;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, nil, self.bounds);
self.bubble.path = path;
[self.layer addSublayer: self.bubble];
}
//Called when timer goes off
- (void) StageGrow {
CGFloat growSize = 50.0;
//Change the size of us and center the circle (but dont want to animate this
[CATransaction begin];
[CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
self.bounds = CGRectMake(0, 0, self.bounds.size.width + growSize, self.bounds.size.height + growSize);
self.bubble.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[CATransaction commit];
[self ActualGrowCircle];
} - (void) ActualGrowCircle {
CGMutablePathRef oldPath = CGPathCreateMutable();
CGPathAddEllipseInRect(oldPath, nil, self.bubble.bounds);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, nil, self.bounds);
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath: #"path"];
animation.duration = 2.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 1;
animation.delegate = self;
animation.autoreverses = NO;
animation.fromValue = (__bridge id) oldPath;
animation.toValue = (__bridge id) path;
self.bubble.bounds = self.bounds;
self.bubble.path = path;
[self.bubble addAnimation: animation forKey: #"animatePath"];
}
Let's say you're growing the bubble from size (100, 100) to size (150, 150).
You create oldPath as an ellipse in the rectangle (0, 0, 100, 100).
You create path as an ellipse in the rectangle (0, 0, 150, 150).
You set the bounds of the bubble layer to (0, 0, 150, 150), and you don't animate this change.
That means that oldPath will appear aligned to the top and left edges of the bubble layer.
One way to fix this is to create oldPath in a rectangle that is centered in the bubble's new bounds:
CGRect oldBubbleRect = CGRectInset(self.bounds, growSize / 2, growSize / 2);
CGPathRef oldPath = CGPathCreateWithEllipseInRect(oldBubbleRect, NULL);
By the way, you are leaking the paths you're creating. You need to call CGPathRelease on them after you're done with them.
Worked it out. In the long run I grouped the animations together and all became good (see code below). Thanks for your help Rob.
-(void)ActualGrowCircle {
CGMutablePathRef newPath = CGPathCreateMutable();
CGPathAddEllipseInRect(newPath, nil, self.bounds);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 1;
animation.autoreverses = NO;
animation.fromValue = (__bridge id)self.bubble.path;
animation.toValue = (__bridge id)newPath;
CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:#"bounds"];
animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation2.repeatCount = 1;
animation2.autoreverses = NO;
animation2.fromValue = [NSValue valueWithCGRect:self.bubble.bounds];
animation2.toValue = [NSValue valueWithCGRect:self.bounds];
CAAnimationGroup *group = [CAAnimationGroup animation];
[group setAnimations:[NSArray arrayWithObjects: animation2, animation, nil]];
group.duration = 0.5;
group.delegate = self;
self.bubble.bounds = self.bounds;
self.bubble.path = newPath;
[self.bubble addAnimation:group forKey:#"animateGroup"];
CGPathRelease(newPath);
}
so my current version looks like this: Animation Movie
i'm fairly new to core-animations, so what i try to achieve is that there are multiple points like the current one, moving from the left box in various angles, heights and speeds out of it, just like a tennis ball machine.
the first problem is, that my "ball" doesn't look like it gets grabbed from gravity and also the speed at first is not fast enough.
also, how to do this animation multiple times with variating distances between the beginning.
if something is unclear, PLEASE leave a comment.
my current code:
- (void)loadView {
[super loadView];
self.view.backgroundColor = [UIColor lightGrayColor];
CGPoint startPoint = CGPointMake(20, 300);
CGPoint endPoint = CGPointMake(300, 500);
UIBezierPath *trackPath = [UIBezierPath bezierPath];
[trackPath moveToPoint:startPoint];
[trackPath addQuadCurveToPoint:endPoint controlPoint:CGPointMake(endPoint.x, startPoint.y)];
CALayer *point = [CALayer layer];
point.bounds = CGRectMake(0, 0, 20.0, 20.0);
point.position = startPoint;
point.contents = (id)([UIImage imageNamed:#"point.png"].CGImage);
[self.view.layer addSublayer:point];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = trackPath.CGPath;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
anim.repeatCount = HUGE_VALF;
anim.duration = 2.0;
[point addAnimation:anim forKey:#"movepoint"];
CALayer *caseLayer = [CALayer layer];
caseLayer.bounds = CGRectMake(0, 0, 140.0, 150.0);
caseLayer.position = startPoint;
caseLayer.contents = (id)([UIImage imageNamed:#"case.png"].CGImage);
[self.view.layer addSublayer:caseLayer];
}
Easiest way to model gravity is with those parabolic equations that you learn in basic physics. In short, the function below takes an initial position, a speed, and an angle (IN RADIANS, where 0 is to the right). a CALayer appears at position and gets shot at that speed and angle.
I'm using a CADisplayLink (which is effectively a timer that synchronizes with your frame rate) to call a function very quickly. Every time the function is called, the point is moved. The horizontal speed is constant and the vertical speed increases towards the bottom every frame to emulate gravity (`vy -= 0.5f; ). If you want more/less gravity, just mess around with this 0.5f value.
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
point = [[CALayer alloc] init];
point.bounds = CGRectMake(0.0f, 0.0f, 10.0f, 10.0f);
point.backgroundColor = [UIColor redColor].CGColor;
point.position = CGPointMake(-10.0f, -10.0f);
[self.layer addSublayer:point];
[point release];
}
return self;
}
-(void)animateBallFrom:(CGPoint)start withSpeed:(CGFloat)speed andAngle:(CGFloat)angle
vx = speed*cos(angle);
vy = speed*sin(angle);
[CATransaction begin];
[CATransaction setDisableActions:YES];
point.position = start;
[CATransaction commit];
displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(animate)];
[displayLink setFrameInterval:2];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
-(void)animate {
if (point.position.x + point.bounds.size.width/2.0f < 0.0f || point.position.x > self.bounds.size.width + point.bounds.size.width/2.0f ||
point.position.y + point.bounds.size.height/2.0f < 0.0f || point.position.y > self.bounds.size.height + point.bounds.size.height/2.0f) {
[displayLink invalidate];
} else {
[CATransaction begin];
[CATransaction setDisableActions:YES];
point.position = CGPointMake(point.position.x+vx, point.position.y-vy);
vy -= 0.5f;
[CATransaction commit];
}
}
and the interface:
#interface Bounce : UIView {
CALayer *point;
CADisplayLink *displayLink;
CGFloat vx, vy;
}
-(void)animateBallFrom:(CGPoint)start withSpeed:(CGFloat)speed andAngle:(CGFloat)angle;
#end
I think there may be a couple of things you can do here.
- One is to use kCAMediaTimingFunctionLinear instead of kCAMediaTimingFunctionEaseOut.
- The other is to set your control point in the center of the ball's fall, wherever that may be. Try this:
CGPoint startPoint = CGPointMake(20, 300);
CGPoint controlPoint = CGPointMake(160, 400);
CGPoint endPoint = CGPointMake(300, 500);
UIBezierPath *trackPath = [UIBezierPath bezierPath];
[trackPath moveToPoint:startPoint];
[trackPath addQuadCurveToPoint:endPoint controlPoint:controlPoint];
CALayer *point = [CALayer layer];
point.bounds = CGRectMake(0, 0, 20.0, 20.0);
point.position = startPoint;
point.contents = (id)([UIImage imageNamed:#"point.png"].CGImage);
[self.view.layer addSublayer:point];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = trackPath.CGPath;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
anim.repeatCount = HUGE_VALF;
anim.duration = 2.0;
[point addAnimation:anim forKey:#"movepoint"];
You should then be able to move the control point around for different paths.
here is my question.
I would like to animate an image, I would like to make it turn around another image, but I don't know how to do that. I just want that it turns( not a rotation but a movement of turning like it is turning around a circle).how can I do this please ?
here is my code :
-(void){
CALayer *orbit1 = [CALayer layer];
orbit1.bounds = CGRectMake(0, 0, 200, 200);
orbit1.position = centre.center;
orbit1.cornerRadius = 100;
orbit1.borderColor= [UIColor redColor].CGColor;
orbit1.borderWidth = 1.5;
planet1 = [CALayer layer];
planet1.bounds = CGRectMake(0, 0, 44.0, 20.0);
planet1.position = CGPointMake(160, 25);
planet1.contents = (id)([UIImage imageNamed:#"bouletest_05.png"].CGImage);
[orbit1 addSublayer:planet1];
CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
anim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
anim1.fromValue = [NSNumber numberWithFloat:0];
anim1.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
anim1.repeatCount = HUGE_VALF;
anim1.duration = 10.0;
[orbit1 addAnimation:anim1 forKey:#"transform"];
[self.view.layer addSublayer:orbit1];
}
and then :
-(void)creation{
imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"abouffer_03.png"]];
[[self view] addSubview:imageView2];
imageView2.center=planet1.center;
}
look at the last line: imageView2.center=planet1.center; the problem is here
Check this post: http://nachbaur.com/blog/core-animation-part-4. There you'll find an example of how to animate view along arbitrary UIBezierPath.
So in your case you'll need to create a circle path around your 'anchor' image:
const CGFloat radius = 100.0f;
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(center.x-radius, center.y-radius, 2*radius, 2*radius)];
and add CAKeyFrameAnimation with that path to the image view you want to move.
I am creating a bookmarking feature for my app, I'd like to show the user what happens in a way similar to itunes store, when you buy something it jumps to tabBar. I once watched some WWDC video that explained this, but can't remember how to do it. Any idea where I should start looking for?
You can take a snapshot of the view you want to animate, then create an image layer, then use Core Animation to animate that to the tab bar. Here's the code I use to do that:
- (void)animateSnapshotOfView:(UIView *)view toTab:(UINavigationController *)navController
{
NSUInteger targetTabIndex = [self.tabBarController.viewControllers indexOfObject:navController];
NSUInteger tabCount = [self.tabBarController.tabBar.items count];
// AFAIK there's no API (as of iOS 4) to get the frame of a tab bar item, so guesstimate using the index and the tab bar frame.
CGRect tabBarFrame = self.tabBarController.tabBar.frame;
CGPoint targetPoint = CGPointMake((targetTabIndex + 0.5) * tabBarFrame.size.width / tabCount, CGRectGetMidY(tabBarFrame));
targetPoint = [self.window convertPoint:targetPoint fromView:self.tabBarController.tabBar.superview];
UIGraphicsBeginImageContext(view.frame.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect frame = [self.window convertRect:view.frame fromView:view.superview];
CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (id)image.CGImage;
imageLayer.opaque = NO;
imageLayer.opacity = 0;
imageLayer.frame = frame;
[self.window.layer insertSublayer:imageLayer above:self.tabBarController.view.layer];
CGMutablePathRef path = CGPathCreateMutable();
CGPoint startPoint = imageLayer.position;
CGPathMoveToPoint(path, NULL, startPoint.x, startPoint.y);
CGPathAddCurveToPoint(path,NULL,
startPoint.x + 100, startPoint.y,
targetPoint.x, targetPoint.y - 100,
targetPoint.x, targetPoint.y);
CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
positionAnimation.path = path;
CGPathRelease(path);
CABasicAnimation *sizeAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
sizeAnimation.fromValue = [NSValue valueWithCGSize:imageLayer.frame.size];
sizeAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(50, 50)];
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = [NSNumber numberWithFloat:0.75];
opacityAnimation.toValue = [NSNumber numberWithFloat:0];
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = [NSArray arrayWithObjects:positionAnimation, sizeAnimation, opacityAnimation, nil];
animationGroup.duration = 1.0;
animationGroup.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animationGroup.delegate = self;
[animationGroup setValue:imageLayer forKey:#"animatedImageLayer"];
[imageLayer addAnimation:animationGroup forKey:#"animateToTab"];
}
I am using CABasicAnimation for animating an image from top to bottom of the screen. I need to
get frame of the image while it is animating from top to bottom......
Code:
-(id)init
{
if(self = [super init])
{
self.title =#"Apple Catch";
mView = [[UIView alloc]initWithFrame:CGRectMake(0,0,320,440)];
// start a timet that will fire 20 times per second
[NSTimer scheduledTimerWithTimeInterval:(0.9) target:self selector:#selector(onTimer)
userInfo:nil repeats:YES];
self.view=mView;
}
return self;
}
- (void)onTimer
{
CGImageRef imageRef = [[UIImage imageNamed:#"apple.png"] CGImage];
int startX = round(random() % 460);
double speed = 1 / round(random() %100) + 1.0;
CALayer *layer = [CALayer layer];
layer.name = #"layer";
layer.contents = imageRef;
layer.frame = CGRectMake(startX, self.view.frame.origin.y, CGImageGetWidth(imageRef),
CGImageGetHeight(imageRef));
[mView.layer addSublayer:layer];
CGPoint start = CGPointMake(startX, 0);
CGPoint end = CGPointMake(startX, self.view.frame.size.height+10);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.delegate = self;
animation.fromValue = [NSValue valueWithCGPoint:start];
animation.toValue = [NSValue valueWithCGPoint:end];
animation.duration = 10*speed;
//animation.repeatCount = repeatCount;
animation.autoreverses = NO;
animation.removedOnCompletion = YES;
animation.fillMode = kCAFillModeForwards;
[layer addAnimation:animation forKey:#"position"];
BOOL intersects = CGRectIntersectsRect(layer.frame , dragger.frame);
printf("\n ==== intersects value :%d",intersects);
}
I need the frame of the image that is animating at each point of it's path on the view.
Can u please suggest the code for this.
Thank U
Use the presentationLayer property of the CALayer to which the animation is applied.
Hi friend
you can try if (CGRectIntersectsRect(layer.frame,dragger.frame) ) {
nslog(#"hiiii");
}