moving a view multiple times - ios

I got an array "coordList" with arrays holding x-y-coordinates.
I want to move a view along these coordinates.
If you have a better way to do this please tell me how to do it.
The way I do it has a big problem. It jumps directley to the last animation and I know why but not how to fix it.
My code:
count = 1;
for(NSArray *array in coordList) {
[UIView animteWithDuration:1 animations:^(void){
CGRect r = [[self.subviews lastObject] frame];
r.origin.x = 103*[coordList[count][0]integerValue];
r.origin.y = 103*[coordList[count][1]integerValue];
[[self.subviews lastObject] setFrame:r];
count++;
[UIView commitAnimations];
}
}
Sorry for my bad english :)

What is happening is that you are committing multiple animations regarding the same view, so each time you commit a new animation for that view it overrides the other existing animations.
You need to set up your animations to trigger one after the other. So a simple for loop won't work that well.
I'd recommend a recursive routine like this:
//set a property to keep current count
#property (nonatomic, asssign) NSInteger currentFrame;
//initialize it to 0 on init
currentFrame = 0;
//call this once when ready to animate
[self animationWithCurrentFrame];
//it will call this routine once for each array.
- (void)animationWithCurrentFrame {
__block NSArray *array = [coordList objectAtIndex:currentFrame];
[UIView animateWithDuration:2.0
animations:^{
CGRect r = [[self.subviews lastObject] frame];
r.origin.x = 103*[array[0]integerValue];
r.origin.y = 103*[array[1]integerValue];
[[self.subviews lastObject] setFrame:r];
}
completion:^(BOOL finished){
currentFrame++;
if (currentFrame < [coordList count]) {
[self animationWithCurrentFrame];
}
}];
}

This is an ideal application of a CAKeyFrameAnimation. And you do not do a bunch of animations, but rather you define a path, and then perform a single animation, specifying that path as the "position":
UIView *viewToAnimate = [self.subviews lastObject];
// create UIBezierPath for your `coordList` array
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(103*[coordList[0][0]integerValue], 103*[coordList[0][1]integerValue])];
for (NSInteger i = 1; i < [coordList count]; i++)
{
[path moveToPoint:CGPointMake(103*[coordList[i][0]integerValue], 103*[coordList[i][1]integerValue])];
}
// now create animation
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.path = [path CGPath];
animation.duration = 2.0;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[viewToAnimate.layer addAnimation:animation forKey:#"position"];
For this to work, you have to add the QuartzCore framework to your project and import the appropriate header at the top of your .m file:
#import <QuartzCore/QuartzCore.h>
For more information, see Keyframe Animation to Change Layer Properties in the Core Animation Programming Guide.
If you're using auto layout, don't forget to reset your constraints for this view when you're done.

Related

Animate path of shape layer

I'm stumped by what I thought would be a simple problem.
I'd like to draw views connected by lines, animate the position of the views and have the connecting line animate too. I create the views, and create a line between them like this:
- (UIBezierPath *)pathFrom:(CGPoint)pointA to:(CGPoint)pointB {
CGFloat halfY = pointA.y + 0.5*(pointB.y - pointA.y);
UIBezierPath *linePath=[UIBezierPath bezierPath];
[linePath moveToPoint: pointA];
[linePath addLineToPoint:CGPointMake(pointA.x, halfY)];
[linePath addLineToPoint:CGPointMake(pointB.x, halfY)];
[linePath addLineToPoint:pointB];
return linePath;
}
-(void)makeTheLine {
CGPoint pointA = self.viewA.center;
CGPoint pointB = self.viewB.center;
CAShapeLayer *lineShape = [CAShapeLayer layer];
UIBezierPath *linePath=[self pathFrom:pointA to:pointB];
lineShape.path=linePath.CGPath;
lineShape.fillColor = nil;
lineShape.opacity = 1.0;
lineShape.strokeColor = [UIColor blackColor].CGColor;
[self.view.layer addSublayer:lineShape];
self.lineShape = lineShape;
}
It draws just how I want it to. My understanding from the docs is that I am allowed to animate a shape's path by altering it in an animation block, like this:
- (void)moveViewATo:(CGPoint)dest {
UIBezierPath *destPath=[self pathFrom:dest to:self.viewB.center];
[UIView animateWithDuration:1 animations:^{
self.viewA.center = dest;
self.lineShape.path = destPath.CGPath;
}];
}
But no dice. The view position animates as expected, but the line connecting to the other view "jumps" right away to the target path.
This answer implies that what I'm doing should work. And this answer suggests a CABasic animation, which seems worse to me since (a) I'd then need to coordinate with the much cooler block animation done to the view, and (b) when I tried it this way, the line didn't change at all....
// worse way
- (void)moveViewATo:(CGPoint)dest {
UIBezierPath *linePath=[self pathFrom:dest to:self.viewB.center];
[UIView animateWithDuration:1 animations:^{
self.viewA.center = dest;
//self.lineShape.path = linePath.CGPath;
}];
CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:#"path"];
morph.duration = 1;
morph.toValue = (id)linePath.CGPath;
[self.view.layer addAnimation:morph forKey:nil];
}
Thanks in advance.
Thanks all for the help. What I discovered subsequent to asking this is that I was animating the wrong property. It turns out, you can replace the layer's shape in an animation, like this:
CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:#"path"];
morph.duration = 1;
morph.fromValue = (__bridge id)oldPath.path;
morph.toValue = (__bridge id)newPath.CGPath;
[line addAnimation:morph forKey:#"change line path"];
line.path=linePath.CGPath;
I guess this is all you need:
#import "ViewController.h"
#interface ViewController ()
//the view to animate, nothing but a simple empty UIView here.
#property (nonatomic, strong) IBOutlet UIView *targetView;
#property (nonatomic, strong) CAShapeLayer *shapeLayer;
#property NSTimeInterval animationDuration;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//the shape layer appearance
self.shapeLayer = [[CAShapeLayer alloc]init];
self.shapeLayer.strokeColor = [UIColor blackColor].CGColor;
self.shapeLayer.fillColor = [UIColor clearColor].CGColor;
self.shapeLayer.opacity = 1.0;
self.shapeLayer.lineWidth = 2.0;
[self.view.layer insertSublayer:self.shapeLayer below:self.targetView.layer];
//animation config
self.animationDuration = 2;
}
- (UIBezierPath *)pathFrom:(CGPoint)pointA to:(CGPoint)pointB {
CGFloat halfY = pointA.y + 0.5*(pointB.y - pointA.y);
UIBezierPath *linePath=[UIBezierPath bezierPath];
[linePath moveToPoint: pointA];
[linePath addLineToPoint:CGPointMake(pointA.x, halfY)];
[linePath addLineToPoint:CGPointMake(pointB.x, halfY)];
[linePath addLineToPoint:pointB];
return linePath;
}
- (void) moveViewTo: (CGPoint) point {
UIBezierPath *linePath= [self pathFrom:self.targetView.center to:point];
self.shapeLayer.path = linePath.CGPath;
//Use CAKeyframeAnimation to animate the view along the path
//animate the position of targetView.layer instead of the center of targetView
CAKeyframeAnimation *viewMovingAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
viewMovingAnimation.duration = self.animationDuration;
viewMovingAnimation.path = linePath.CGPath;
//set the calculationMode to kCAAnimationPaced to make the movement in a constant speed
viewMovingAnimation.calculationMode =kCAAnimationPaced;
[self.targetView.layer addAnimation:viewMovingAnimation forKey:viewMovingAnimation.keyPath];
//draw the path, animate the keyPath "strokeEnd"
CABasicAnimation *lineDrawingAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
lineDrawingAnimation.duration = self.animationDuration;
lineDrawingAnimation.fromValue = [NSNumber numberWithDouble: 0];
lineDrawingAnimation.toValue = [NSNumber numberWithDouble: 1];
[self.shapeLayer addAnimation:lineDrawingAnimation forKey:lineDrawingAnimation.keyPath];
//This part is crucial, update the values, otherwise it will back to its original state
self.shapeLayer.strokeEnd = 1.0;
self.targetView.center = point;
}
//the IBAction for a UITapGestureRecognizer
- (IBAction) viewDidTapped:(id)sender {
//move the view to the tapped location
[self moveViewTo:[sender locationInView: self.view]];
}
#end
Some explanation:
For UIViewAnimation, the property value is changed when the
animation is completed. For CALayerAnimation, the property value is
never change, it is just an animation and when the animation is
completed, the layer will go to its original state (in this case, the
path).
Putting self.lineShape.path = linePath.CGPath doesn't work is
because self.linePath is a CALayer instead of a UIView, you
have to use CALayerAnimation to animate a CALayer
To draw a path, it's better to animate the path drawing with keyPath
strokeEnd instead of path. I'm not sure why path worked in the
original post, but it seems weird to me.
CAKeyframeAnimation (instead of CABasicAnimation or UIViewAnimation) is used to animate the view along the path. (I guess you would prefer this to the linear animation directly from start point to end point). Setting calculationMode to kCAAnimationPaced will give a constant speed to the animation, otherwise the view moving will not sync with the line drawing.

CAKeyframeAnimation not animating until I rotate device

I seem to be missing the obvious when animating a key frame. I have looked at many code samples including Apple's MoveMe, referenced in the CAKeyframeAnimation documentation, yet, I cant find a discrepancy that would cause what I'm seeing.
I create a CGMutablePathRef, then a CAKeyframeAnimation and set it to animate an image view along the path. An animation group is created so I can remove the view when done.
Yet, my animation never shows up. UNTIL I rotate the device. It seems a relayout of the view causes the animation to kickstart. I tried the obvious like [theImageView setNeedsDisplay] or even setNeedsLayout, and on the container view as well. Yet, still cant get it to work when I need to. They only show up when I rotate the device.
In the following, -cgPathFromArray: takes an NSArray of internal opcodes which is converted into a CGPathRef. Its verified to be correct because when I rotate the device, the animation does show along the programmed path.
- (void) animateImage: (NSString*) imageName
onPath: (NSArray*) path
withDuration: (NSString*) duration
{
if (self.sceneView)
{
CGMutablePathRef animationPath = [self cgPathFromArray: path];
if (animationPath)
{
UIImage* image = [self findImage: imageName];
if (image)
{
UIImageView* imageView = [[UIImageView alloc] initWithFrame: CGRectMake(0, 0, image.size.width, image.size.height)];
if (imageView)
{
CAKeyframeAnimation* keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath: #"position"];
imageView.image = image;
[self.sceneView addSubview: imageView];
keyFrameAnimation.removedOnCompletion = YES;
keyFrameAnimation.fillMode = kCAFillModeForwards;
keyFrameAnimation.duration = duration.floatValue;
keyFrameAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionLinear];
keyFrameAnimation.repeatCount = 0;
keyFrameAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[keyFrameAnimation setPath: animationPath];
//group animation with termination block to cleanup
CAAnimationGroup* group = [CAAnimationGroup animation];
group.duration = keyFrameAnimation.duration;
group.removedOnCompletion = YES;
group.fillMode = kCAFillModeForwards;
group.animations = #[keyFrameAnimation];
CorpsAnimationCompletionBlock theBlock = ^void(void)
{
[imageView removeFromSuperview];
};
[group setValue: theBlock
forKey: kCorpsAnimationCompletionBlock];
group.delegate = self;
[imageView.layer addAnimation: group
forKey: nil];
}
}
}
}
}
Anyone can help with this?
You are probably having trouble because you're adding an animation to a layer in the same transaction where the layer is added to the visible layer tree. Core Animation doesn't like to attach animations to layers that haven't been committed yet. You may be able to work around this by doing [CATransaction flush] after adding the layer.
Your code is rather hard to look at because of the excessive nesting. Consider using early returns to make it more readable.
Also, you're explicitly creating the same frame that the -[UIImage initWithImage:] initializer would create for you.
If you're using an animation group and setting a delegate simply so you can execute a block at the end of the animation, there is an easier way. You can begin a CATransaction, set the transaction's completion block, then add the animation, then commit the transaction.
Thus:
- (void) animateImage:(NSString *)imageName onPath: (NSArray *)path
withDuration: (NSString *)duration
{
if (!self.sceneView)
return;
CGMutablePathRef animationPath = [self cgPathFromArray:path];
if (!animationPath)
return;
UIImage *image = [self findImage:imageName];
if (!image)
return;
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.sceneView addSubview: imageView];
// commit the implicit transaction so we can add an animation to imageView.
[CATransaction flush];
[CATransaction begin]; {
[CATransaction setCompletionBlock:^{
[imageView removeFromSuperview];
}];
CAKeyframeAnimation *animation = [CAKeyframeAnimation
animationWithKeyPath:#"position"];
animation.duration = duration.floatValue;
animation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
animation.path = animationPath;
[imageView.layer addAnimation:animation forKey:animation.keyPath];
} [CATransaction commit];
}

UIview Flip darkens the Views

I've been implementing a simple FlipView in iOS : A UIView that contains two subviews, displaying one at a time, and when you click on it, it flips them.
I'm using the following to animate the flipping.
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
#synchronized(self){
if(!self.flipping){
self.flipping = YES;
UIView *toView = self.currentView == self.primaryView ? self.secondaryView : self.primaryView;
[UIView transitionFromView:self.currentView toView:toView duration:self.speed options:UIViewAnimationOptionTransitionFlipFromLeft|UIViewAnimationOptionCurveEaseInOut completion:^(BOOL finished) {
[self.currentView removeFromSuperview];
self.currentView = toView;
self.flipping = NO;
}];
}
}
}
Pretty straight forward, right ?
But what bugs me is that, while the views are flip, the flipped content is darkened. Which shows, against a light background.
Would anyone knows a solution to have the exact same animation, but without the darkening (<= is that even a word ?)
Thanks in advance !
PS : I'm targeting IOS 5 and above.
I recently had a problem with similar symptoms and I was adding a subview over and over again else where in my code whenever I committed a certain action. Maybe you are doing something similar? When your touches end, are you doing something else to your flipped content? You probably need to remove the subviews being added IF that is your problem.
I succeeded, getting inspiration in the code I found here http://www.mycodestudio.com/blog/2011/01/10/coreanimation/ (and he, himself, took inspiration from http://www.mentalfaculty.com/mentalfaculty/Blog/Entries/2010/9/22_FLIPPIN_OUT_AT_NSVIEW.html)
Anyway, what I do spin between two views.
- (void)flip{
#synchronized(self){
if(!self.flipping){
self.flipping = YES;
UIView *bottomView = self.currentView == self.primaryView ? self.secondaryView : self.primaryView;
CALayer *top = self.currentView.layer;
CALayer *bot = bottomView.layer;
CAAnimation *topAnimation = [self flipAnimationWithDuration:self.speed/2.0 forLayerBeginningOnTop:YES scaleFactor:1];
CAAnimation *bottomAnimation = [self flipAnimationWithDuration:self.speed/2.0 forLayerBeginningOnTop:NO scaleFactor:1];
CGFloat zDistance = 1500.0f;
CATransform3D perspective = CATransform3DIdentity;
perspective.m34 = -1. / zDistance;
top.transform = perspective;
bot.transform = perspective;
topAnimation.delegate = self;
[CATransaction setCompletionBlock:^{
[top removeAllAnimations];
[self.currentView removeFromSuperview];
self.currentView = bottomView;
[self addSubview:bottomView];
[CATransaction setCompletionBlock:^{
self.flipping = NO;
[bot removeAllAnimations];
}];
[CATransaction begin];
[bot addAnimation:bottomAnimation forKey:#"flip"];
[CATransaction commit];
}];
[CATransaction begin];
[top addAnimation:topAnimation forKey:#"flip"];
[CATransaction commit];
}
}
}
-(CAAnimation *)flipAnimationWithDuration:(NSTimeInterval)aDuration forLayerBeginningOnTop:(BOOL)beginsOnTop scaleFactor:(CGFloat)scaleFactor
{
// Rotating halfway (pi radians) around the Y axis gives the appearance of flipping
CABasicAnimation *flipAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
CGFloat startValue = beginsOnTop ? 0.0f : M_PI/2;
CGFloat endValue = beginsOnTop ? -M_PI/2 : 0.0f;
flipAnimation.fromValue = [NSNumber numberWithDouble:startValue];
flipAnimation.toValue = [NSNumber numberWithDouble:endValue];
// Shrinking the view makes it seem to move away from us, for a more natural effect
// Can also grow the view to make it move out of the screen
CABasicAnimation *shrinkAnimation = nil;
if (scaleFactor != 1.0 ) {
shrinkAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
shrinkAnimation.toValue = [NSNumber numberWithFloat:scaleFactor];
// We only have to animate the shrink in one direction, then use autoreverse to "grow"
shrinkAnimation.duration = aDuration * 0.5;
shrinkAnimation.autoreverses = YES;
}
// Combine the flipping and shrinking into one smooth animation
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = [NSArray arrayWithObjects:flipAnimation, shrinkAnimation, nil];
// As the edge gets closer to us, it appears to move faster. Simulate this in 2D with an easing function
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:beginsOnTop?kCAMediaTimingFunctionEaseIn:kCAMediaTimingFunctionEaseOut];
animationGroup.duration = aDuration;
// this really means keep the state of the object at whatever the anim ends at
// if you don't do this then it reverts back to the original state (e.g. brown layer)
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.removedOnCompletion = NO;
return animationGroup;
}
The two views are named primaryView and secondaryView. You can use any view, (ImageView, text view...)

Animate UIView height from bottom to top

I'm doing a simple animation of UIView height so that it reveals.
By default it seems to be revealing from top to bottom, and I want it to reveal bottom to top.
I have the UIView anchored to the bottom of the screen.
I'm sure it something simple i'm missing..... any tips?
Thanks
I really think the simplest way to accomplish this would be to animate BOTH the height and the y properties of the view. If they happen along the same curve, it should look completely seamless to the user. As you are animating the height to 0, also animate the y component to the original y + the original height.
UIView *view = ...;
float originalY = view.frame.origin.y;
float originalH = view.bounds.size.height;
[UIView animateWithDuration:1.2f delay:1.0f options:UIViewAnimationCurveEaseInOut animations:^{
view.frame = CGRectMake(view.frame.origin.x, (originalY + originalH), view.bounds.size.width, 0);
}completion:^(BOOL finished) {
NSLog(#"Animation is complete");
}];
I believe this would give the look and feel of a collapsing view. I haven't tried this out in code, but I see no reason why it wouldn't be possible like this.
hide under bottom
[self animateViewHeight:myView withAnimationType:kCATransitionFromBottom];
for reverse animation
[self animateViewHeight:myView withAnimationType:kCATransitionFromTop];
...
- (void)animateViewHeight:(UIView*)animateView withAnimationType:(NSString*)animType {
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionPush];
[animation setSubtype:animType];
[animation setDuration:0.5];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[[animateView layer] addAnimation:animation forKey:kCATransition];
animateView.hidden = !animateView.hidden;
}
Like a dog with a bone I figured this out....
Instead of animating the frame height, I applied a transform to the view and set the anchor point of the layer.
//set the anchor point to the bottom of the view
[self setAnchorPoint:CGPointMake(0.5, 1.0) forView:hostView];
//Scale the height to close to zero
hostView.transform = CGAffineTransformMakeScale(1, 0.00001);
If I put 0 as the y scale, the view behaves weird.... at the end of the animation i just set it to hidden.
On the way back up I just use the Identity Transform (reset it)
hostView.transform = CGAffineTransformIdentity;
Note that changing my anchor point shifted the position of my view. See this post for the setAnchorPoint method which normalises the view after setting the anchorPoint
Changing my CALayer's anchorPoint moves the view
Instead you could try putting it in a view with clipsToBounds = YES and then animate it from the bottom to the middle of the view, like so:
viewToAnimate.frame = CGRectMake(viewToAnimate.frame.origin.x,
viewToAnimate.superview.frame.size.height,
viewToAnimate.frame.size.width,
viewToAnimate.frame.size.height);
[UIView animateWithDuration:0.5 animations:^{
viewToAnimate.center = viewToAnimate.superview.center;
}];
This way, you don't have to set the height to 0, and it solves any problems with autoresizing within the view.
As requested, this is the code that I'm using... I'm using a CAKeyFrameAnimation, which may be a bit more than what you're looking for. It would probably work the same with a CABasicAnimation, I'm just showing you this code because I already have it written.
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
springLayer = [[CALayer alloc] init];
springLayer.backgroundColor = [UIColor redColor].CGColor;
springLayer.anchorPoint = CGPointMake(0, 1);
springLayer.frame = CGRectMake(125, 285, 100, 115);
[springLayer setNeedsDisplay];
[self.layer addSublayer:springLayer];
[self test];
}
return self;
}
-(void)test {
CAKeyframeAnimation *heightAnim = [[CAKeyframeAnimation alloc] init];
heightAnim.duration = 3;
heightAnim.removedOnCompletion = NO;
heightAnim.fillMode = kCAFillModeForwards;
heightAnim.beginTime = CACurrentMediaTime() + 0.25;
NSMutableArray *v = [[NSMutableArray alloc] init];
NSMutableArray *t = [[NSMutableArray alloc] init];
float dest = 250;
float difference = 135;
while (difference > 1.0) {
[v addObject:[NSNumber numberWithFloat:dest-difference]];
[t addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
difference *= 0.7;
[v addObject:[NSNumber numberWithFloat:dest+difference]];
[t addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
difference *= 0.7;
}
heightAnim.values = v;
heightAnim.timingFunctions = t;
[springLayer addAnimation:heightAnim forKey:#"bounds.size.height"];
}
one way I've done it with an AdWhirlView, hide it below the screen, then animate it up;
AdWhirlView *adWhirlView = [AdWhirlView requestAdWhirlViewWithDelegate:self];
adWhirlView.delegate = self;
adWhirlView.frame = CGRectMake(0, 430+kAdWhirlViewHeight, kAdWhirlViewWidth, kAdWhirlViewHeight);
[self.parentViewController.view insertSubview:adWhirlView belowSubview:self.view];
[UIView beginAnimations:#"AdWhirlIn" context:nil];
[UIView setAnimationDuration:.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
adWhirlView.frame = CGRectMake(0, 430, kAdWhirlViewWidth, kAdWhirlViewHeight);
[UIView commitAnimations];

Moving Animation of a CALayer?

I have a array of CALayers that i'm looping and trying to move.
Tile is a CALayer subclass and has a CGRect property named originalFrame where i store the frame i want to animate to.
When i'm useing the code below everything is instant moved to the correct possition and there is no 4 sec animation. How can i animate these CALayer?
for (int i = 0; i < [tileArray count]; i++) {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDelay:i];
[UIView setAnimationDuration:4];
Tile *currentCard = (Tile*)[tileArray objectAtIndex:i];
currentCard.frame = currentCard.originalFrame;
[UIView commitAnimations];
}
You have two problems: the first is that you're trying to animate the layer's frame directly. Since this is a derived property, you can't do that. Instead, you have to animate the position property. http://developer.apple.com/library/mac/#qa/qa1620/_index.html
Second, you're using UIView's +beginAnimations API, but you say your Tile objects are CALayers, not UIViews. So you don't need to use +beginAnimations. Instead you need to use a CAAnimation object, like CABasicAnimation (untested):
for (Tile *tile in tileArray)
{
static NSString * const kProperty = #"position";
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:kProperty];
animation.duration = 4.0f;
animation.fromValue = [tile valueForKey:kProperty];
animation.toValue = [NSValue valueWithCGRect:tile.originalFrame];
[tile addAnimation:animation forKey:kProperty];
}

Resources