Search code examples
iphoneobjective-ciosuiviewcore-animation

Trouble with CAAnimation


// header file for the card class

#import "OWCardView.h"

@implementation OWCardView;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.layer.contents      = (id) [UIImage imageNamed:@"4.png"].CGImage;
        self.layer.shadowColor   = [UIColor whiteColor].CGColor;
        self.layer.shadowOpacity = 0.3;
    }
    return self;
}


- (void) moveCard:(float)xPoint along:(float)yPoint
{
    CGRect someRect = CGRectMake(xPoint, yPoint, 72, 96);

    [UIView animateWithDuration: 3.5
                          delay: 0.3
                        options: UIViewAnimationCurveEaseOut
                     animations: ^{ self.frame = someRect;}
                     completion: ^(BOOL finished) { }];
}


@end


// part of the Implementation in the view controller's viewDidLoad

[...]
     CGRect myImageRect = CGRectMake(20.0f, 20.0f, 72.0f, 96.0f);

     // myCard is a property of the viewController
     self.myCard = [[OWCardView alloc] initWithFrame:myImageRect];
     [self.view addSubview:self.myCard];
     [self.myCard moveCard: 240 along: 355];     // First call
     [self.myCard moveCard: 50 along: 355];      // Second call
     [self.myCard moveCard: 50 along: 50];       // Third call
[...]

Problem: Only the last message to moveCard in the viewController is executed; the first and second calls do not execute. What is wrong with the code?

When I comment the third message to moveCard out, the second call is executed and the first does not. If I comment the second and third calls out, then the first call executes. But I want all three animations to occur on the card.


Solution

  • The problem that you have is that the animation is asynchronous.

    The code does not wait for each animation to finish before executing the next one. So it blasts through the first two calls and starts the third animation immediately and cancels out the first two meaning that you'll only see the last animation.

    You have to use the completed block to do something after the animation has finished.

    The easy way to do this is like so...

    [UIView animateWithDuration: 3.5
                          delay: 0.3
                        options: UIViewAnimationCurveEaseOut
                     animations: ^{ self.frame = CGRectMake(240, 355, 72, 96);}
                     completion: ^(BOOL finished) {
        [UIView animateWithDuration: 3.5
                              delay: 0.3
                            options: UIViewAnimationCurveEaseOut
                         animations: ^{ self.frame = CGRectMake(50, 355, 72, 96);}
                         completion: ^(BOOL finished) {
            [UIView animateWithDuration: 3.5
                                  delay: 0.3
                                options: UIViewAnimationCurveEaseOut
                             animations: ^{ self.frame = CGRectMake(50, 50, 72, 96);}
                             completion: ^(BOOL finished) {
                //All finished
            }];
        }];
    }];
    

    You could put this into some sort of recursive function but it's probably just easier to do this.

    The other alternative is to use key frame animation where you literally build up the path and timing step by step and then say "GO" and it will do the animation you have built up.

    This is probably easier for now though and I've used this kind of thing before.