3

I'm writing a toy app to experiment with some Core Animation features, including animating along a path (that's where the Sun's movement comes in) and manipulating time.

https://github.com/boredzo/WatchCompass

Animated screenshot of the app, showing the sun and the clock's hour hand going around.

(Never mind the button, which isn't implemented yet.)

The sun and watch face are CALayers, each containing a static image. The hour hand is a CAShapeLayer within the watch face layer, with its anchor point set to one end ((NSPoint){ 0.5, 1.0 }).

The sun is animated using a CAKeyframeAnimation along a path. The ellipse shows the path; you can see that they're not lined up for some reason, but that's a different question.

The hour hand's transform.rotation.z is animated using a CABasicAnimation, as described in this answer.

The problem—at least the one I'm asking about in this question—is the difference in duration.

Both animations are set to exactly the same duration, but the sun arrives back at its starting position a full two clock-hours before the hour hand does.

Of course, eventually the clock's duration will be exactly half the sun's duration (or its speed set to 2), since a clock only has 12 hours. If I do that, then the hour hand falls 4 clock-hours behind the sun, rather than 2.

Screenshot of the clock going around twice per virtual day and arriving four clock-hours late.

So, given that both animations have the same duration, or the duration of the clock's animation is an even multiple of the sun's animation, why does the clock take longer?

For that matter, although I'm not complaining, why does the sun wait for the clock to catch up?

Community
  • 1
  • 1
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • No idea about that - generic suggestion: have you checked if sun's path have several last segments computed wrong for some reason and having 0 length (all in the same point)? – Alexei Levenkov Feb 03 '14 at 03:06
  • @AlexeiLevenkov: I have not. The path is simply created with `CGPathCreateWithEllipseInRect`, so I would be surprised if it has more than two anchor points. – Peter Hosey Feb 03 '14 at 03:12
  • I see. Rob Rix suggestion sounds right to me (As it is the second way to have wrong timing on last segments)... – Alexei Levenkov Feb 03 '14 at 03:27
  • @AlexeiLevenkov: All the same, thanks for the suggestion, as the code I wrote to log those values (which I ended up not posting because Rob had posted his answer by then) helped me figure out the other problem. – Peter Hosey Feb 03 '14 at 04:41
  • @PeterHosey Hi, the other problem meaning the one that has to deal with the `CGPath` not displaying correctly as the animation path in `CAKeyedFrameAnimation`? What causes that? I recently noticed the same in my animation, too. Thanks. – Unheilig Mar 11 '14 at 20:25

1 Answers1

7

This appears to be due to the fact that you aren’t specifying a value for the keyTimes property of the keyframe animation. Per the documentation:

For the best results, the number of elements in the array should match the number of elements in the values property or the number of control points in the path property. If they do not, the timing of your animation might not be what you expect.

Indeed, setting keyTimes to @[ @0, @0.25, @0.5, @0.75, @1 ] appears to correct this.

Rob Rix
  • 358
  • 1
  • 8
  • Didn't realize I *have* to set that even when using a path. But you're right: it lines up perfectly with the clock now. – Peter Hosey Feb 03 '14 at 03:34
  • 1
    I think what’s probably happening is that either `CAKeyframeAnimation` is calculating the keyframe timing steps with the reciprocal of the number of control points in the path (five), yielding six keyframes, i.e. `@[ @0, @0.2, @0.4, @0.6, @0.8, @1 ]`, which indeed behaves as the above animation shows. I believe this may be due to an assumption about open vs. closed paths. – Rob Rix Feb 03 '14 at 03:50
  • Or simply the number of elements, which is six: `moveto curveto curveto curveto curveto closepath`. (I originally said it was also five, but I'd forgotten that I subtract the `closepath` when I count the number of elements for my own computation of the `keyTimes`.) – Peter Hosey Feb 03 '14 at 04:35
  • @RobRix Hi, what does it mean "with the reciprocal of the number of control points"? Something like if there is 5, it would become 1/5? Thanks in advance. – Unheilig Mar 11 '14 at 20:21