11

I want to access get the value of the transform scale at a point in time. Here is the animation creation :

    CABasicAnimation *grow = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    grow.toValue = @1.5f;
    grow.duration = 1;
    grow.autoreverses = YES;
    grow.repeatCount = HUGE_VALF;
    [view.layer addAnimation:grow forKey:@"growAnimation"];

I'd like to get, for example when a user presses a button, the current size of the view. Logging the frame or the bounds always returns constant values. Any help would be much appreciated !

DCMaxxx
  • 2,534
  • 2
  • 25
  • 46
  • You have to look at `presentationLayer`. This should answer your question: http://stackoverflow.com/a/15487126/653513 – Rok Jarc Nov 27 '13 at 14:27
  • Try logging frame of presentation layer: `NSLog (@"Frame: %@",NSStringFromCGRect(yourView.layer.presentationLayer.frame));` – Rok Jarc Nov 27 '13 at 14:49
  • Same as above, still get constant values. I can see the view animating though... – DCMaxxx Nov 27 '13 at 15:01

4 Answers4

17

In Core Animation if an animation is "in flight" on a layer, the layer has a second layer property known as presentationLayer. That layer contains the values of the in-flight animation.

Edited

(With a nod to Mohammed, who took the time to provide an alternate answer for Swift)

Use code like this:

Objective-C

CGFloat currentScale = [[layer.presentationLayer valueForKeyPath: @"transform.scale"] floatValue];

Swift:

let currentScale = layer.presentation()?.value(forKeyPath: "transform.scale") ?? 0.0

Note that the Swift code above will return 0 if the layer does not have a presentation layer. That's a fail case that you should program for. It might be better to leave it an optional and check for a nil return.

Edit #2:

Note that as Kentzo pointed out in their comment, reading a value from an in-flight animation will give you a snapshot at an instant in time. The new value of the parameter you read (transform.scale in this example) will start to deviate from the reading you get. You should either pause the animation, or use the value you read quickly and get another value each time you need it.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Keep in mind, that the animation will keep running in the background after the value of the presentation layer is read. It may and will become out of date possibly causing "jumps" if used as a starting point for new animations. To avoid this, [pause](https://stackoverflow.com/a/3003922/188530) the running animation first. – Kentzo Jan 13 '20 at 21:26
  • Good point. The above code will provide a snapshot and will only be correct for the instant at which it's run. – Duncan C Jan 13 '20 at 21:54
  • Yup. It's very easy to forget that this instant is independent of app's main thread. – Kentzo Jan 13 '20 at 23:43
  • Alternative approach to pausing _all_ animations is to immediately add a temporary animation with `fromValue` set to current value of the `presentationLayer` and `speed` set to `0`. – Kentzo Jan 15 '20 at 04:37
9

The CALayer documentation describes presentationLayer quite clearly:

The layer object returned by this method provides a close approximation of the layer that is currently being displayed onscreen. While an animation is in progress, you can retrieve this object and use it to get the current values for those animations.

caughtinflux
  • 897
  • 7
  • 19
5

swift version of @Duncan C answer will be:

let currentValue = someView.layer.presentation()?.value(forKeyPath: "transform.scale")
MEH
  • 1,777
  • 3
  • 15
  • 34
0

Swift 5: The nicest answer, in a function from Hacking With Swift

func scale(from transform: CGAffineTransform) -> Double {
    return sqrt(Double(transform.a * transform.a + transform.c * transform.c))
}
RanLearns
  • 4,086
  • 5
  • 44
  • 81