Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 815
Stoner Avatar asked Oct 06 '22 14:10

Stoner


1 Answers

When you animate a view, any previous animation you applied to the view is immediately cancelled, even if either animation has a non-zero delay. So if you want to apply sequential animations, you have to add the second animation after the first has ended. You can use the completion block to detect when the first animation ends.

It's pretty easy to make moveCard:along: maintain a queue of target rectangles and apply the animations sequentially:

@implementation OWCardView {
    NSMutableArray *pendingRects;
    BOOL isAnimating;
}

- (void)moveCard:(float)xPoint along:(float)yPoint {
    CGRect someRect = CGRectMake(xPoint, yPoint, 72, 96);
    if (!pendingRects) {
        pendingRects = [NSMutableArray array];
    }
    [pendingRects addObject:[NSValue valueWithCGRect:someRect]];
    [self startAnimatingIfNeeded];
}

- (void)startAnimatingIfNeeded {
    if (isAnimating)
        return;
    if (pendingRects.count == 0)
        return;
    CGRect rect = [[pendingRects objectAtIndex:0] CGRectValue];
    [pendingRects removeObjectAtIndex:0];

    isAnimating = YES;
    [UIView animateWithDuration:3.5 delay:0.3 options:UIViewAnimationCurveEaseOut animations:^{
        self.frame = rect;
    } completion:^(BOOL finished) {
        isAnimating = NO;
        [self startAnimatingIfNeeded];
    }];
}
like image 167
rob mayoff Avatar answered Oct 10 '22 03:10

rob mayoff