3

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.

jeilsoft
  • 572
  • 5
  • 13
  • 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 Oct 26 '14 at 14:14
  • 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 Oct 26 '14 at 15:57
  • Whats wrong with using CATransaction? – Teevus Mar 15 '16 at 04:57

3 Answers3

6

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];
    });
}
jeilsoft
  • 572
  • 5
  • 13
1

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.

Oliver Atkinson
  • 7,970
  • 32
  • 43
  • yes, but my animations are not so simple. there are a ton of dynamic animations and they should be removed after animation is completed. – jeilsoft Oct 28 '14 at 13:00
0

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"];
   }
}
aquarium_moose
  • 355
  • 1
  • 11
  • I know this way, 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. – jeilsoft Oct 26 '14 at 13:46
  • animationDidStop never be called, now – jeilsoft Oct 26 '14 at 14:55
  • You don't _have_ to set both `fromValue` and `endValue` (or perhaps `byValue`). It all depends on how you are configuring the animation compared to what the model value for that key path is. – David Rönnqvist Oct 26 '14 at 15:52