CABasicAnimation: What if CAShapeLayer position not changed? - ios

I'm really new on the CABasicAnimation topic. If you want to animate the x
position of an CAShapeLayer from the current x position to a specific x position you should
use the following:
NSLog(#"CABasicAnimation now...");
CABasicAnimation *layerPositionAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
[layerPositionAnimation setDuration:0.330];
[layerPositionAnimation setRemovedOnCompletion: NO];
[layerPositionAnimation setFillMode: kCAFillModeForwards];
[layerPositionAnimation setTimingFunction: [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[layerPositionAnimation setFromValue:[NSValue valueWithCGPoint:CGPointMake(self.shapeLayer.position.x, self.shapeLayer.position.y)]];
[layerPositionAnimation setToValue:[NSValue valueWithCGPoint:CGPointMake(14.0, self.shapeLayer.position.y)]];
[self.shapeLayer addAnimation:layerPositionAnimation forKey:#"layerPositionAnimation"];
In my case the self.shapeLayer is a CAShapeLayer used as a simple mask:
[[overlayTopShadow layer] setMask:self.shapeLayer];
But what if the animation never happens (x position not changed)? What i'm doing wrong in my code?
Any suggestions, ideas, examples?
Thanks a lot

Related

How to get current position of animating uiview in ios

I want current position of animating UIView during animation and I am using this code.
CABasicAnimation * m_pAnimationObj = [CABasicAnimation animationWithKeyPath:#"transform.translation"];
[m_pAnimationObj setFromValue:[NSValue valueWithCGPoint:CGPointMake(0,-50)]];
[m_pAnimationObj setToValue:[NSValue valueWithCGPoint:CGPointMake(0,[UIScreen mainScreen].bounds.size.height+100)]];
m_pAnimationObj.duration = 2.0;
m_pAnimationObj.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[DropImage.layer addAnimation:m_pAnimationObj forKey:#"animations"];
Please answer if anyone knows about this.
Thank you in advance!
Use this code:
CGRect currentCenter = [drawingView.layer.presentationLayer center];
or
CGRect currentFrame = [DropImage.layer.presentationLayer frame];

Animate UIView using Core Animation?

I am having a little trouble getting a UiView to animate properly using core animation. Here is my code:
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithCGPoint:self.view.layer.position]];
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(250, self.view.layer.position.y)]]; //I want to move my UIView 250 pts to the right
[animation setDuration:1];
[animation setDelegate:self]; //where self is the view controller of the view I want to animate
[animation setRemovedOnCompletion:NO];
[self.view.layer addAnimation:animation forKey:#"toggleMenu"]; //where self.view returns the view I want to animate
I have also implemented the following delegate method:
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if(flag)
[self.view.layer setTransform:CATransform3DMakeTranslation(250, 0, 0)]; //set layer to its final position
}
I am trying to make it so that the UIView moves 250 points to the right. However, when the animation is triggered, my view starts to move but the animations seems to end before the view moves 250 points to the right, resulting in the UIView 'teleporting' to its final position. I can't seem to figure out what is causing this behavior.
I have also tried using the UIView method +(void)animateWithDuration:animations: and this approach works perfectly. However, I am trying to achieve a subtle 'bounce' effect and I'd much rather achieve it using the setTimingFunction:functionWithControlPoints: method rather than having multiple callbacks.
Any help and suggestions are appreciated.
Try like this
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
rotationAnimation.fromValue=[NSValue valueWithCGPoint:CGPointMake([view center].x, [view center].y)];
rotationAnimation.toValue=[NSValue valueWithCGPoint:CGPointMake([view center].x+250, [view center].y)];
rotationAnimation.duration = 1.0+i;
rotationAnimation.speed = 3.0;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[view.layer addAnimation:rotationAnimation forKey:#"position"];
If you are aiming to move your view, your code should go like this:
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithCGPoint:self.view.layer.position]];
CGPoint p = self.view.layer.position;
p.x += 250;
self.view.layer.position = p;
[animation setDuration:1];
[animation setRemovedOnCompletion:NO];
[self.view.layer addAnimation:animation forKey:#"toggleMenu"];
You should really change your view's(layer's) position. Otherwise, your view will stay still at its previous position when your animation removed. That's a difference between UIView Animation and Core Animation.
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setFromValue:[NSValue valueWithCGPoint:view.layer.position]];
CGPoint newPosition = CGPointMake(200, 300);
view.layer.position = p;
[animation setDuration:0.5f];
[animation setRemovedOnCompletion:NO];
[group setFillMode:kCAFillModeForwards];
[[view layer] addAnimation:animation forKey:#"position"];

Cannot get current position of CALayer during animation

I am trying to achieve an animation that when you hold down a button it animates a block down, and when you release, it animates it back up to the original position, but I cannot obtain the current position of the animating block no matter what. Here is my code:
-(IBAction)moveDown:(id)sender{
CGRect position = [[container.layer presentationLayer] frame];
[movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];
[movePath addLineToPoint:CGPointMake(container.frame.origin.x, 310)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = NO;
moveAnim.fillMode = kCAFillModeForwards;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
animGroup.duration = 2.0;
animGroup.removedOnCompletion = NO;
animGroup.fillMode = kCAFillModeForwards;
[container.layer addAnimation:animGroup forKey:nil];
}
-(IBAction)moveUp:(id)sender{
CGRect position = [[container.layer presentationLayer] frame];
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];
[movePath addLineToPoint:CGPointMake(container.frame.origin.x, 115)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = NO;
moveAnim.fillMode = kCAFillModeForwards;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
animGroup.duration = 2.0;
animGroup.removedOnCompletion = NO;
animGroup.fillMode = kCAFillModeForwards;
[container.layer addAnimation:animGroup forKey:nil];
}
But the line
CGRect position = [[container.layer presentationLayer] frame];
is only returning the destination position not the current position. I need to basically give me the current position of the container thats animating once I release the button, so I can perform the next animation. What I have now does not work.
I haven't analyzed your code enough to be 100% sure why [[container.layer presentationLayer] frame] might not return what you expect. But I see several problems.
One obvious problem is that moveDown: doesn't declare movePath. If movePath is an instance variable, you probably want to clear it or create a new instance each time moveDown: is called, but I don't see you doing that.
A less obvious problem is that (judging from your use of removedOnCompletion and fillMode, in spite of your use of presentationLayer) you apparently don't understand how Core Animation works. This turns out to be surprisingly common, so forgive me if I'm wrong. Anyway, read on, because I will explain how Core Animation works and then how to fix your problem.
In Core Animation, the layer object you normally work with is a model layer. When you attach an animation to a layer, Core Animation creates a copy of the model layer, called the presentation layer, and the animation changes the properties of the presentation layer over time. An animation never changes the properties of the model layer.
When the animation ends, and (by default) is removed, the presentation layer is destroyed and the values of the model layer's properties take effect again. So the layer on screen appears to “snap back” to its original position/color/whatever.
A common, but wrong way to fix this is to set the animation's removedOnCompletion to NO and its fillMode to kCAFillModeForwards. When you do this, the presentation layer hangs around, so there's no “snap back” on screen. The problem is that now you have the presentation layer hanging around with different values than the model layer. If you ask the model layer (or the view that owns it) for the value of the animated property, you'll get a value that's different than what's on screen. And if you try to animate the property again, the animation will probably start from the wrong place.
To animate a layer property and make it “stick”, you need to change the model layer's property value, and then apply the animation. That way, when the animation is removed, and the presentation layer goes away, the layer on screen will look exactly the same, because the model layer has the same property values as its presentation layer had when the animation ended.
Now, I don't know why you're using a keyframe to animate straight-line motion, or why you're using an animation group. Neither seems necessary here. And your two methods are virtually identical, so let's factor out the common code:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromValue];
animation.toValue = [NSValue valueWithCGPoint:toValue];
animation.duration = 2;
[layer addAnimation:animation forKey:animation.keyPath];
}
Notice that we're giving the animation a key when I add it to the layer. Since we use the same key every time, each new animation will replace (remove) the prior animation if the prior animation hasn't finished yet.
Of course, as soon as you play with this, you'll find that if you moveUp: when the moveDown: is only half finished, the moveUp: animation will appear to be at half speed because it still has a duration of 2 seconds but only half as far to travel. We should really compute the duration based on the distance to be travelled:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromValue];
animation.toValue = [NSValue valueWithCGPoint:toValue];
animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY);
[layer addAnimation:animation forKey:animation.keyPath];
}
If you really need it to be a keypath animation in an animation group, your question should show us why you need those things. Anyway, it works with those things too:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:fromValue];
[path addLineToPoint:toValue];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.path = path.CGPath;
animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY);
CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = animation.duration;
group.animations = #[animation];
[layer addAnimation:group forKey:animation.keyPath];
}
You can find the full code for my test program in this gist. Just create a new Single View Application project and replace the contents of ViewController.m with the contents of the gist.

Animate drawing the fill of a CAShapeLayer

I've been playing around with drawing a path using a CAShapeLayer as outlined in this great article, http://oleb.net/blog/2010/12/animating-drawing-of-cgpath-with-cashapelayer, but I'm wondering if there's a way to animate the filling of a layer.
For example, I have some text I want to draw on the screen, but I've only been able to draw the stroke of the text and not the fill. Another example, I have a star shape that I would like to animate it being filled in.
Is this possible using a CAShapeLayer or other object?
Thanks!
Its most of the time the same code, you just have to set different values for fromValue and toValue of your CABasicAnimation. I created a category which returns me a CABasicAnimation:
Animation for StrokeEnd
+ (CABasicAnimation *)animStrokeEndWithDuration:(CGFloat)dur
delegate:(id)target{
CABasicAnimation *animLine =
[CABasicAnimation animationWithKeyPath:#"strokeEnd"];
[animLine setDuration:dur];
[animLine setFromValue:[NSNumber numberWithFloat:0.0f]];
[animLine setToValue:[NSNumber numberWithFloat:1.0f]];
[animLine setRemovedOnCompletion:NO];
[animLine setFillMode:kCAFillModeBoth];
[animLine setDelegate:target];
[animLine setTimingFunction:
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
return animLine;
}
Animation for fillColor
+ (CABasicAnimation *)animFillColorWithDur:(CGFloat)dur
startCol:(UIColor *)start
endColor:(UIColor *)end
delegate:(id)target{
CABasicAnimation *animFill =
[CABasicAnimation animationWithKeyPath:#"fillColor"];
[animFill setDuration:dur];
[animFill setFromValue:(id)start.CGColor];
[animFill setToValue:(id)end.CGColor];
[animFill setRemovedOnCompletion:NO];
[animFill setDelegate:target];
[animFill setFillMode:kCAFillModeBoth];
return animFill;
}
The returned CABasicAnimation just has to be added to a CAShapeLayer:
[_myShapeLayer addAnimation:returnedAnimation forKey:#"animKey"]
Yes, it is possible.
CAShapeLayers have a fillColor property which is animatable.
It works the same way as changing the strokeEnd / strokeStart like you've already done with with your animation.

How to do a curve/arc animation with CAAnimation?

I have an user interface where an item get deleted, I would like to mimic the "move to folder" effect in iOS mail. The effect where the little letter icon is "thrown" into the folder. Mine will get dumped in a bin instead.
I tried implementing it using a CAAnimation on the layer. As far as I can read in the documentations I should be able to set a byValue and a toValue and CAAnimation should interpolate the values. I am looking to do a little curve, so the item goes through a point a bit above and to the left of the items start position.
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position"];
[animation setDuration:2.0f];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut]];
[animation setFromValue:[NSValue valueWithCGPoint:fromPoint]];
[animation setByValue:[NSValue valueWithCGPoint:byPoint]];
[animation setToValue:[NSValue valueWithCGPoint:CGPointMake(512.0f, 800.0f)]];
[animation setRepeatCount:1.0];
I played around with this for some time, but it seems to me that Apple means linear interpolation.
Adding the byValue does not calculate a nice arc or curve and animate the item through it.
How would I go about doing such an animation?
Thanks for any help given.
Using UIBezierPath
(Don't forget to link and then import QuartzCore, if you're using iOS 6 or prior)
Example code
You could use an animation that will follow a path, conveniently enough, CAKeyframeAnimation supports a CGPath, which can be obtained from an UIBezierPath. Swift 3
func animate(view : UIView, fromPoint start : CGPoint, toPoint end: CGPoint)
{
// The animation
let animation = CAKeyframeAnimation(keyPath: "position")
// Animation's path
let path = UIBezierPath()
// Move the "cursor" to the start
path.move(to: start)
// Calculate the control points
let c1 = CGPoint(x: start.x + 64, y: start.y)
let c2 = CGPoint(x: end.x, y: end.y - 128)
// Draw a curve towards the end, using control points
path.addCurve(to: end, controlPoint1: c1, controlPoint2: c2)
// Use this path as the animation's path (casted to CGPath)
animation.path = path.cgPath;
// The other animations properties
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.duration = 1.0
animation.timingFunction = CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseIn)
// Apply it
view.layer.add(animation, forKey:"trash")
}
Understanding UIBezierPath
Bezier paths (or Bezier Curves, to be accurate) work exactly like the ones you'd find in photoshop, fireworks, sketch... They have two "control points", one for each vertex. For example, the animation I just made:
Works the bezier path like that. See the documentation on the specifics, but it's basically two points that "pull" the arc towards a certain direction.
Drawing a path
One cool feature about UIBezierPath, is that you can draw them on screen with CAShapeLayer, thus, helping you visualise the path that it will follow.
// Drawing the path
let *layer = CAShapeLayer()
layer.path = path.cgPath
layer.strokeColor = UIColor.black.cgColor
layer.lineWidth = 1.0
layer.fillColor = nil
self.view.layer.addSublayer(layer)
Improving the original example
The idea of calculating your own bezier path, is that you can make the completely dynamic, thus, the animation can change the curve it's going to do, based on multiple factors, instead of just hard-coding as I did in the example, for instance, the control points could be calculated as follows:
// Calculate the control points
let factor : CGFloat = 0.5
let deltaX : CGFloat = end.x - start.x
let deltaY : CGFloat = end.y - start.y
let c1 = CGPoint(x: start.x + deltaX * factor, y: start.y)
let c2 = CGPoint(x: end.x , y: end.y - deltaY * factor)
This last bit of code makes it so that the points are like the previous figure, but in a variable amount, respect to the triangle that the points form, multiplied by a factor which would be the equivalent of a "tension" value.
You are absolutely correct that animating the position with a CABasicAnimation causes it to go in a straight line. There is another class called CAKeyframeAnimation for doing more advanced animations.
An array of values
Instead of toValue, fromValue and byValue for basic animations you can either use an array of values or a complete path to determine the values along the way. If you want to animate the position first to the side and then down you can pass an array of 3 positions (start, intermediate, end).
CGPoint startPoint = myView.layer.position;
CGPoint endPoint = CGPointMake(512.0f, 800.0f); // or any point
CGPoint midPoint = CGPointMake(endPoint.x, startPoint.y);
CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:#"position"];
move.values = #[[NSValue valueWithCGPoint:startPoint],
[NSValue valueWithCGPoint:midPoint],
[NSValue valueWithCGPoint:endPoint]];
move.duration = 2.0f;
myView.layer.position = endPoint; // instead of removeOnCompletion
[myView.layer addAnimation:move forKey:#"move the view"];
If you do this you will notice that the view moves from the start point in a straight line to the mid point and in another straight line to the end point. The part that is missing to make it arc from start to end via the mid point is to change the calculationMode of the animation.
move.calculationMode = kCAAnimationCubic;
You can control it arc by changing the tensionValues, continuityValues and biasValues properties. If you want finer control you can define your own path instead of the values array.
A path to follow
You can create any path and specify that the property should follow that. Here I'm using a simple arc
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL,
startPoint.x, startPoint.y);
CGPathAddCurveToPoint(path, NULL,
controlPoint1.x, controlPoint1.y,
controlPoint2.x, controlPoint2.y,
endPoint.x, endPoint.y);
CAKeyframeAnimation *move = [CAKeyframeAnimation animationWithKeyPath:#"position"];
move.path = path;
move.duration = 2.0f;
myView.layer.position = endPoint; // instead of removeOnCompletion
[myView.layer addAnimation:move forKey:#"move the view"];
Try this it will solve your problem definitely, I have used this in my project:
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(10, 126, 320, 24)] autorelease];
label.text = #"Animate image into trash button";
label.textAlignment = UITextAlignmentCenter;
[label sizeToFit];
[scrollView addSubview:label];
UIImageView *icon = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"carmodel.png"]] autorelease];
icon.center = CGPointMake(290, 150);
icon.tag = ButtonActionsBehaviorTypeAnimateTrash;
[scrollView addSubview:icon];
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.center = CGPointMake(40, 200);
button.tag = ButtonActionsBehaviorTypeAnimateTrash;
[button setTitle:#"Delete Icon" forState:UIControlStateNormal];
[button sizeToFit];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[scrollView addSubview:button];
[scrollView bringSubviewToFront:icon];
- (void)buttonClicked:(id)sender {
UIView *senderView = (UIView*)sender;
if (![senderView isKindOfClass:[UIView class]])
return;
switch (senderView.tag) {
case ButtonActionsBehaviorTypeExpand: {
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"transform"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim.duration = 0.125;
anim.repeatCount = 1;
anim.autoreverses = YES;
anim.removedOnCompletion = YES;
anim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)];
[senderView.layer addAnimation:anim forKey:nil];
break;
}
case ButtonActionsBehaviorTypeAnimateTrash: {
UIView *icon = nil;
for (UIView *theview in senderView.superview.subviews) {
if (theview.tag != ButtonActionsBehaviorTypeAnimateTrash)
continue;
if ([theview isKindOfClass:[UIImageView class]]) {
icon = theview;
break;
}
}
if (!icon)
return;
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:icon.center];
[movePath addQuadCurveToPoint:senderView.center
controlPoint:CGPointMake(senderView.center.x, icon.center.y)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = YES;
CABasicAnimation *scaleAnim = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleAnim.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
scaleAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1.0)];
scaleAnim.removedOnCompletion = YES;
CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:#"alpha"];
opacityAnim.fromValue = [NSNumber numberWithFloat:1.0];
opacityAnim.toValue = [NSNumber numberWithFloat:0.1];
opacityAnim.removedOnCompletion = YES;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
animGroup.duration = 0.5;
[icon.layer addAnimation:animGroup forKey:nil];
break;
}
}
}
I found out how to do it. It's really possible to animate X and Y separately. If you animate them over the same time (2.0 seconds below) and set a different timing function, it will make it look like it moves in an arc instead of a straight line from start to finish values. To adjust the arc you'd need to play around with setting a different timing function. Not sure if CAAnimation supports any "sexy" timing functions however.
const CFTimeInterval DURATION = 2.0f;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"position.y"];
[animation setDuration:DURATION];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[animation setFromValue:[NSNumber numberWithDouble:400.0]];
[animation setToValue:[NSNumber numberWithDouble:0.0]];
[animation setRepeatCount:1.0];
[animation setDelegate:self];
[myview.layer addAnimation:animation forKey:#"animatePositionY"];
animation = [CABasicAnimation animationWithKeyPath:#"position.x"];
[animation setDuration:DURATION];
[animation setRemovedOnCompletion:NO];
[animation setFillMode:kCAFillModeForwards];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
[animation setFromValue:[NSNumber numberWithDouble:300.0]];
[animation setToValue:[NSNumber numberWithDouble:0.0]];
[animation setRepeatCount:1.0];
[animation setDelegate:self];
[myview.layer addAnimation:animation forKey:#"animatePositionX"];
Edit:
Should be possible to change timing function by using https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/CAMediaTimingFunction_class/Introduction/Introduction.html (CAMediaTimingFunction inited by functionWithControlPoints:::: )
It's a "cubic Bezier curve". I'm sure Google has answers there. http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves :-)
I have had a bit similar question several days ago and I implemented it with timer, as Brad says, but not NSTimer. CADisplayLink - that is the timer which should be used for this purpose, as it is synchronized with the frameRate of the application and provides smoother and more natural animation. You can look at my implementation of it in my answer here. This technique really gives much more control on animation than CAAnimation, and is not much more complicated.
CAAnimation can't draw anything since it doesn't even redraw the view. It only moves, deforms and fades what is already drawn.

Resources