3
votes

there.

in iOS app, Core animation callback don't work.

- (void)startAnim {
    CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    anim.fromValue = startAngle;
    anim.toValue = endAngle;
    anim.duration = 2;
    anim.delegate = self;

    [self.target addAnimation:anim forKey:nil];   // self.target is CALayer instance, it's sublayer of Custom UIView
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
   [self.target setValue:@(endAngle) forKeyPath:@"transform.rotation.z"];
}

But animationDidStop never be called. If I change the code like as following, completion blocked is called.

- (void)startAnim {
    CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    anim.fromValue = startAngle;
    anim.toValue = endAngle;
    anim.duration = 2;
    anim.delegate = self;

    [CATransaction begin];
    [CATransaction setCompletionBlock:^{
        [self.target setValue:@(endAngle) forKeyPath:@"transform.rotation.z"];
    }];

    [self.target addAnimation:anim forKey:nil];
    [CATransaction commit];
}

But I don't want to use CATransaction. Why is not animationDidStop called?

Update: There is a way to set final value like as

- (void)startAnim {
    CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    anim.fromValue = startAngle;
    anim.toValue = endAngle;
    anim.duration = 2;
    anim.delegate = self;

    [self.target setValue:@(endAngle) forKeyPath:@"transform.rotation.z"];
    [self.target addAnimation:anim forKey:nil];
}

But final assignment should be done when the animation is finished. Because there are multiple dynamic animations of layer, so I don't know final value.

3
Is your animation actually running? I try the exactly same code in a empty UIViewController on a UIView's layer and animationDidStop:finished: is called correctly. - Kevin Hirsch
I would advice you to rethink and I would actually recommend that you update the model value when you add the animation. That value will always reflect the value you are heading at and eventually will end up at. If there are "multiple dynamic animations" involved I would perhaps consider using "additive" animations or using byValue to get away from having to keep track of explicit to- and from values. With multiple animations you will get multiple didStop callbacks and doing the right thing in there could end up being more complicated and hard to maintain. - David Rönnqvist
Whats wrong with using CATransaction? - Teevus

3 Answers

6
votes

I found the reason why animationDidStop is not called.

Because animation was added in loop of other thread,

So I fixed like as following.

- (void)startAnim {
    dispatch_async(dispatch_get_main_queue(), ^{
        CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        anim.fromValue = startAngle;
        anim.toValue = endAngle;
        anim.duration = 2;
        anim.delegate = self;

        [self.target addAnimation:anim forKey:nil];
    });
}
1
votes

To me it sounds like you dont want the animation to reset its position, this is quite simple and achieved with a couple lines of code when setting up the animation.

It can be easily placed in your code like such:

anim.fillMode = kCAFillModeForwards; anim.removedOnCompletion = NO;

What this means is when your animation has finished it will remain at the end and any further animations will be from that state.

0
votes

Last year I use animation of UIView instead CABasicAnimation, but if my memory does not fail you have to set endAngle befor add animation, so try this:

- (void)startAnim {
    CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    anim.fromValue = startAngle;
    anim.toValue = endAngle;
    anim.duration = 2;
    anim.delegate = self;

    [self.target setValue:@(endAngle) forKeyPath:@"transform.rotation.z"];
    [self.target addAnimation:anim forKey:nil];
}

UPD:

You are setting anim.toValue = endAngle; before start animation, so its not good that end value changes after animation complete. Anyway you can set it again in animationDidStop

- (void)startAnim {
    CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    anim.fromValue = startAngle;
    anim.toValue = endAngle;
    anim.duration = 2;
    anim.delegate = self;

    intermediateAngle = endAngle;

    [self.target setValue:@(endAngle) forKeyPath:@"transform.rotation.z"];
    [self.target addAnimation:anim forKey:nil];
}

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
   if (intermediateAngle != endAngle) {
      startAngle = intermediateAngle;
      [self startAnim]; // or just [self.target setValue:@(endAngle) forKeyPath:@"transform.rotation.z"];
   }
}