Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get a CAAnimation to call a block every animation tick?

Can I somehow have a block execute on every "tick" of a CAAnimation? It could possibly work like the code below. If there's a way to do this with a selector, that works too.

NSString* keyPath = @"position.x";
CGFloat endValue = 100;

[CATransaction setDisableActions:YES];
[self.layer setValue:@(toVal) forKeyPath:keyPath];

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
animation.duration = 1;
animation.delegate = self;

__weak SelfClass* wself = self;
animation.eachTick = ^{
    NSLog(@"Current x value: %f", [wself valueForKeyPath:keypath]);
};

[self.layer addAnimation:animation forKey:nil];
like image 377
aleclarson Avatar asked Mar 29 '14 05:03

aleclarson


1 Answers

The typical technique is to employ a display link (CADisplayLink).

So, define a property for the display link:

@property (nonatomic, strong) CADisplayLink *displayLink;

And then implement the methods to start, stop, and handle the display link:

- (void)startDisplayLink
{
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)stopDisplayLink
{
    [self.displayLink invalidate];
    self.displayLink = nil;
}

- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
    CALayer *layer = self.animatedView.layer.presentationLayer;

    NSLog(@"%@", NSStringFromCGRect(layer.frame));
}

Note, while a view is animating, you cannot look at its basic properties (because it will reflect the end destination rather than the "in flight" position), but rather you access the animated view's layer's presentationLayer, as shown above.

Anyway, start the display link when you start your animation. And remove it upon completion.

With standard block-based animation, you'd do something like:

[UIView animateWithDuration:2.0 delay:0 options:0 animations:^{
    view.frame = someNewFrame;
} completion:^(BOOL finished) {
    [self stopDisplayLink];
}];

[self startDisplayLink];

With Core Animation, it might look like:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:startPoint];
animation.toValue = [NSValue valueWithCGPoint:endPoint];

[CATransaction setAnimationDuration:1.0];
[CATransaction setCompletionBlock:^{
    [self stopDisplayLink];
}];

[CATransaction begin];
[self.animatedView.layer addAnimation:animation forKey:nil];
[CATransaction commit];

[self startDisplayLink];
like image 179
Rob Avatar answered Nov 01 '22 07:11

Rob