How to properly animate sequentially using CAAnimation
Asked Answered
B

0

2

I'm simply closing doors, delaying 2 sec/calling a method then opening them back. One comes from the left side and the other from the right side. I first used UIView animation block but then realized whenever user leaves the app during animation, its completion block would never get called.

I could not find anyone complaining about such behavior when interruption happens during animation thus could not solve the problem and now I'm hoping CAAnimation's animationDidStop or CATransaction's completion block will get called no matter what 100%.

EDIT

Tried below code and it worked even though I couldn't make the delay work. (CACurrentMediaTime()+delay makes the second animation end immediately) Most importantly, I'm happy that animationDidStop ALWAYS gets called even if animation is interrupted. (Block-based UIView animations' completion blocks never get called if user leaves the app OR a VC's animation is fired at the same time) I would love to know why this happens with UIView animations and if there's a solution to this.

Should I stick with this method and try to fix the delay or come up with some other approach? CAAnimationGroup doesn't work in this case because animationDidStop gets called when the whole group is finished hence can't call methods when the first animation is ended.

-(void)action {

CABasicAnimation *animDoorLeftClose = [CABasicAnimation animationWithKeyPath:@"position.x"];
animDoorLeftClose.fromValue = [NSNumber numberWithFloat:self.doorLeft0.position.x];
animDoorLeftClose.toValue = [NSNumber numberWithFloat:self.doorLeft0.position.x + self.frame.size.width/2];
animDoorLeftClose.duration = 0.2;
animDoorLeftClose.beginTime = 0.0;
self.doorLeft0.position = CGPointMake(self.doorLeft0.position.x + self.frame.size.width/2, self.doorLeft0.position.y);
[self.doorLeft0 addAnimation:animDoorLeftClose forKey:@"leftClosing"];

CABasicAnimation *animDoorRightClose = [CABasicAnimation animationWithKeyPath:@"position.x"];
[animDoorRightClose setValue:@"doorClosing" forKey:@"id"];
animDoorRightClose.fromValue = [NSNumber numberWithFloat:self.doorRight0.position.x];
animDoorRightClose.toValue = [NSNumber numberWithFloat:self.doorRight0.position.x - self.frame.size.width/2];
animDoorRightClose.duration = 0.2;
animDoorRightClose.beginTime = 0.0;
animDoorRightClose.delegate = self;
self.doorRight0.position = CGPointMake(self.doorRight0.position.x - self.frame.size.width/2, self.doorRight0.position.y);
[self.doorRight0 addAnimation:animDoorRightClose forKey:@"rightClosing"];    }




-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {

if([[anim valueForKey:@"id"] isEqual:@"doorClosing"]) {

            [self.delegate shipTOmap];
    }
    [self doorClosed];
}

else if ([[anim valueForKey:@"id"] isEqual:@"doorOpening"]) {

    [self doorOpened];
}}




-(void)doorClosed {

CABasicAnimation *animDoorLeftOpen = [CABasicAnimation animationWithKeyPath:@"position.x"];
animDoorLeftOpen.fromValue = [NSNumber numberWithFloat:[self.doorLeft0.presentationLayer position].x];
animDoorLeftOpen.toValue = [NSNumber numberWithFloat:[self.doorLeft0.presentationLayer position].x - self.frame.size.width/2];
animDoorLeftOpen.duration = 0.4;
animDoorLeftOpen.beginTime = CACurrentMediaTime()+0.2;
self.doorLeft0.position = CGPointMake([self.doorLeft0.presentationLayer position].x - self.frame.size.width/2, self.doorLeft0.position.y);
[self.doorLeft0 addAnimation:animDoorLeftOpen forKey:@"leftOpening"];

CABasicAnimation *animDoorRightOpen = [CABasicAnimation animationWithKeyPath:@"position.x"];
[animDoorRightOpen setValue:@"doorOpening" forKey:@"id"];
animDoorRightOpen.fromValue = [NSNumber numberWithFloat:[self.doorRight0.presentationLayer position].x];
animDoorRightOpen.toValue = [NSNumber numberWithFloat:[self.doorRight0.presentationLayer position].x + self.frame.size.width/2];
animDoorRightOpen.duration = 0.4;
animDoorRightOpen.beginTime = CACurrentMediaTime()+0.2;
animDoorRightOpen.delegate = self;
self.doorRight0.position = CGPointMake([self.doorRight0.presentationLayer position].x + self.frame.size.width/2, self.doorRight0.position.y);
[self.doorRight0 addAnimation:animDoorRightOpen forKey:@"rightOpening"];
}
Babin answered 9/4, 2016 at 13:21 Comment(7)
So, you want to animate them closing, wait 1 second, the open again. And if the user leaves the application and comes back, the doors should be open again? Or should they be closed?Heat
If you're animating "position.x" then the values should only be the x coordinate, not a point.Heat
The closing values might not be what you'd expect. Since the model values never change (at least what I could see), the value of doorRight.center is not the position it appears to have on screen. That's also one of the reasons I'd generally recommend against specifying removedOnCompletion = NO.Heat
Yes I'd like the whole animation to be ended IF user left the app then came back but what I must have is for the completion block to be called. Block-based UIView animations fail to call completion blocks if animation is interrupted and I'm just hoping CAAnimation does call completion blocks even if animation were interrupted.Babin
And using points with position.x does work perfectly but okay I should fix it then. I've tried setting removeOnCompletion to Yes and No and when Yes, the second animation gets called then immediately ends. Setting it No keeps the doors closed and doesn't animate the second one.Babin
And yes the closing values aren't right in the above code, forgot to post the fixed version. Fixed them later then tried it with the correct values but still the same result.Babin
Whaaa figured out what's wrong with my code. I didn't know I had to set a new position for the model layer BEFORE adding animation to it.Babin

© 2022 - 2024 — McMap. All rights reserved.