Does animation end callback for CALayer?

I am wondering where the callbacks (or if any) for animations in CALayer. In particular, for implied animations, such as changing the frame, position, etc. In UIView, you can do something like this:

[UIView beginAnimations:@"SlideOut" context:nil]; [UIView setAnimationDuration:.3]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(animateOut:finished:context:)]; CGRect frame = self.frame; frame.origin.y = 480; self.frame = frame; [UIView commitAnimations]; 

In particular, setAnimationDidStopSelector is what I want for animation in CALayer. Is there anything similar?

TIA.

+66
iphone core-animation
Nov 17 '08 at 21:17
source share
11 answers

I answered my question. You must add the animation using CABasicAnimation as follows:

 CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:@"frame"]; anim.fromValue = [NSValue valueWithCGRect:layer.frame]; anim.toValue = [NSValue valueWithCGRect:frame]; anim.delegate = self; [layer addAnimation:anim forKey:@"frame"]; 

And implement the delegate method animationDidStop:finished: and you should be good to go. Thank god this functionality exists !: D

+61
Nov 17 '08 at 21:41
source share

You can use CATransaction, it has a completion block handler.

 [CATransaction begin]; CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; [pathAnimation setDuration:1]; [pathAnimation setFromValue:[NSNumber numberWithFloat:0.0f]]; [pathAnimation setToValue:[NSNumber numberWithFloat:1.0f]]; [CATransaction setCompletionBlock:^{_lastPoint = _currentPoint; _currentPoint = CGPointMake(_lastPoint.x + _wormStepHorizontalValue, _wormStepVerticalValue);}]; [_pathLayer addAnimation:pathAnimation forKey:@"strokeEnd"]; [CATransaction commit]; 
+123
Jun 30 '12 at 3:28
source share

Spent 4 hours with this garbage, just to fade away. Pay attention to the comment in the code.

  [CATransaction begin]; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"]; animation.duration = 0.3; animation.fromValue = [NSNumber numberWithFloat:0.0f]; animation.toValue = [NSNumber numberWithFloat:1.0f]; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeBoth; /// [box addAnimation:animation forKey:@"j"]; Animation will not work if added here. Need to add this only after the completion block. [CATransaction setCompletionBlock:^{ CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"opacity"]; animation2.duration = 0.3; animation2.beginTime = CACurrentMediaTime()+1; animation2.fromValue = [NSNumber numberWithFloat:1.0f]; animation2.toValue = [NSNumber numberWithFloat:0.0f]; animation2.removedOnCompletion = NO; animation2.fillMode = kCAFillModeBoth; [box addAnimation:animation2 forKey:@"k"]; }]; [box addAnimation:animation forKey:@"j"]; [CATransaction commit]; 
+49
Oct 30 '14 at 1:29
source share

Here is the answer in Swift 3.0 based on bennythemink solution:

  // Begin the transaction CATransaction.begin() let animation = CABasicAnimation(keyPath: "strokeEnd") animation.duration = duration //duration is the number of seconds animation.fromValue = 0 animation.toValue = 1 animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) circleLayer.strokeEnd = 1.0 // Callback function CATransaction.setCompletionBlock { print("end animation") } // Do the actual animation and commit the transaction circleLayer.add(animation, forKey: "animateCircle") CATransaction.commit() 
+47
Sep 24 '16 at 0:43
source share

For 2018 ...

It couldn't be simpler.

Do not forget [weak self] , otherwise you will fall.

 func animeExample() { CATransaction.begin() let a = CABasicAnimation(keyPath: "fillColor") a.fromValue, duration = ... etc etc CATransaction.setCompletionBlock{ [weak self] in self?.animeExample() self?.ringBell() print("again...") } someLayer.add(a, forKey: nil) CATransaction.commit() } 

,,,, NOTE. CRITICAL COUNCIL >>>>>

We are interrupting this post for critical advice:

There MUST be a string setCompletionBlock BEFORE the string someLayer.add.

You can google to read more details about this, but in general it is important to have it in that order. Now back to the post!

& Lt; & Lt; & Lt; & Lt; & Lt; NOTE. - CRITICAL COUNCIL., ,,.

In this example, it just calls itself again.

Of course, you can call any function.




Notes for anyone new to iOS animation:

  1. The "key" (as in forKey ) does not matter and is rarely used . Set it to zero. If you want to install it, set it to any string.

  2. "KeyPath" is actually the actual "thing you animate . " This is literally a layer property, such as "opacity", "backgroundColor", etc., but written as a string . (You cannot just enter โ€œwhatever you wantโ€ there, it must be the name of the actual property of the layer, and it must be animatable.)

We repeat: the "key" (rarely used - just set it to zero) and "keyPath" are completely unrelated to each other.

You often see sample code where the two are confused (thanks to a silly naming convention), which causes all kinds of problems.




Note that you can use a delegate one at a time, but itโ€™s much easier to just use a completion block, because (A) it is self-sufficient and can be used anywhere, and (B) you usually have several animes, in this case using a delegate is a bore

+40
Nov 28 '17 at 16:45
source share

Just pay attention to those who find this page on Google: you can really do this job by setting the "delegate" property of your animation object to the object that receives the notification and implements the "animationDidStop" method in this object .m file. I just tried it and it works. I do not know why Joe Bloov said that this is not the right way.

+10
Sep 25 '14 at 21:41
source share

In Swift 4+, I just added delegate as

 class CircleView: UIView,CAAnimationDelegate { ... let animation = CABasicAnimation(keyPath: "strokeEnd") animation.delegate = self//Set delegate 

Animation completion callback -

 func animationDidStop(_ anim: CAAnimation, finished flag: Bool) { print("Animation END") } 
+10
Jan 02 '18 at 11:41
source share

Swift 5.0

 func blinkShadow(completion: @escaping (() -> Void)) { CATransaction.begin() let animation = CABasicAnimation(keyPath: "shadowRadius") animation.fromValue = layer.shadowRadius animation.toValue = 0.0 animation.duration = 0.1 animation.autoreverses = true CATransaction.setCompletionBlock(completion) layer.add(animation, forKey: nil) CATransaction.commit() } 
+1
Apr 11 '19 at 0:51
source share

You can set the name of this animation when configuring the CAAnimation object. In animationDiStop: finished, just compare the name of the Animation object provided to perform certain functions based on the animation.

0
Nov 19 '09 at 18:54
source share

If you do not want to mess with CATransaction . I created an extension that encapsulates a callback in an animation object.

The code can be found in this snippet: https://gitlab.com/snippets/1786298

And you can use it as follows:

 let appearAnimation = CASpringAnimation(keyPath: "transform") appearAnimation.fromValue = contentView.layer.transform appearAnimation.toValue = CATransform3DIdentity appearAnimation.mass = 0.65 appearAnimation.duration = appearAnimation.settlingDuration appearAnimation.isAdditive = true appearAnimation.onCompletion { completed() } 
0
Dec 02 '18 at 8:49
source share

Also, in my case, as a delegate, I had an open UIView class, and animationDidStop: Finish was not called. I got rid of the "public" and now it works. In case some of you may have this problem.

0
Dec 21 '18 at 14:15
source share



All Articles