-->

Get instant value of a CABasicAnimation

2019-07-30 02:00发布

问题:

While i'm implementing a game, i'm just front of a matter, whose really easy i think, but don't know how to fix it. I'm a bit new in objective-c as you could see with my reputation :(

The problem is, i have an animation, which works correctly. Here is the code :

CABasicAnimation * bordgauche = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
bordgauche.fromValue = [NSNumber numberWithFloat:0.0f];
bordgauche.toValue = [NSNumber numberWithFloat:749.0f];
bordgauche.duration = t;
bordgauche.repeatCount = 1;
[ImageSuivante.layer addAnimation:bordgauche forKey:@"bordgauche"];

And i want to get the current position of my image. So i use :

CALayer *currentLayer = (CALayer *)[ImageSuivante.layer presentationLayer];
currentX = [(NSNumber *)[currentLayer valueForKeyPath:@"transform.translation.x"] floatValue];

But i don't get it instantly. I get one time "Current = 0.0000", which is the starting value, when i use a nslog to print it, but not the others after. I don't know how to get the instant position of my image, currentX, all the time.

I expect i was understable. Thanks for your help :)

回答1:

I think you have 3 options here (pls comment if more exist):

option1: split your first animation into two and when the first half ends start the second half of the animation plus the other animation

...
bordgauche.toValue = [NSNumber numberWithFloat:749.0f / 2];
bordgauche.duration = t/2;
bordgauche.delegate = self // necessary to catch end of anim
[bordgauche setValue:@"bordgauche_1" forKey: @"animname"]; // to identify anim if more exist

...

- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
    if ([theAnimation valueForKey: @"animname"]==@"bordgauche_1") {
         CABasicAnimation * bordgauche = [CABasicAnimation
                  animationWithKeyPath:@"transform.translation.x"];
         bordgauche.fromValue = [NSNumber numberWithFloat:749.0f / 2];
         bordgauche.toValue = [NSNumber numberWithFloat:749.0f];
         bordgauche.duration = t/2;
         bordgauche.repeatCount = 1;
         [ImageSuivante.layer addAnimation:bordgauche forKey:@"bordgauche_2"];


         // plus start your second anim 
}

option2: setup a NSTimer or a CADisplayLink (this is better) callback and check continuously the parameters of your animating layer. Test the parameters for the required value to trigger the second anim.

displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(check_ca_anim)];
[displayLink setFrameInterval:1];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

... or
animationTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(1.0 / 60.0) target:self selector:@selector(check_ca_anim) userInfo:nil repeats:TRUE];
[[NSRunLoop mainRunLoop] addTimer:animationTimer forMode:NSRunLoopCommonModes];

- (void) check_ca_anim {
    ...
    CGPoint currentPosition = [[ImageSuivante.layer presentationLayer] position];
    // test it and conditionally start something
    ...
}

option3: setup a CADisplayLink (can be called now as "gameloop") and manage the animation yourself by calculating and setting the proper parameters of the animating object. Not knowing what kind of game you would like to create I would say game loop might be useful for other game specific reasons. Also here I mention Cocos2d which is a great framework for game development.



回答2:

You can get the value from your layer's presentation layer.

CGPoint currentPos = [ImageSuivante.layer.presentationLayer position];
NSLog(@"%f %f",currentPos.x,currentPos.y);


回答3:

You can try getting the value from your layer's presentation layer, which should be close to what is being presented on screen:

[ [ ImageSuivante.layer presentationLayer ] valueForKeyPath:@"transform.translation.x" ] ;


回答4:

I would add one option to what @codedad pointed out. What's interesting, it gives a possibility of getting instant values of your animating layer precisely (you can see CALayer's list of animatable properties) and it's very simple.

If you override method

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;

in your UIView class (yep, you'd need to implement a custom class derived from UIView for your "ImageSuivante" view), then the parameter layer which is passed there would give you exactly what you need. It contains precise values used for drawing.

E.g. in this method you could update a proper instant variable (to work with later) and then call super if you don't do drawing with you hands:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
    self.instantOpacity = layer.opacity;
    self.instantTransform = layer.transform;
    [super drawLayer:layer inContext:context];
}

Note that layer here is generally not the same object as self.layer (where self is your custom UIView), so e.g. self.layer.opacity will give you the target opacity, but not the instant one, whereas self.instantOpacity after the code above will contain the instant value you want.

Just be aware that this is a drawing method and it may be called very frequently, so no unnecessary calculations or any heavy operations there.

The source of this idea is Apple's Custom Animatable Property project.