Objective-C - CABasicAnimation applying changes after animation?

I use CABasicAnimation to move and resize the image. I want the image to be added to the supervisor, animation, and then removed from the supervisor.

To achieve this, I listen to the delegate call of my CAAnimationGroup , and as soon as it is called, I remove the image view from the supervisor.

The problem is that sometimes the image blinks at its original location before being removed from the supervisor. What is the best way to avoid this behavior?

 CAAnimationGroup *animGroup = [CAAnimationGroup animation]; animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil]; animGroup.duration = .5; animGroup.delegate = self; [imageView.layer addAnimation:animGroup forKey:nil]; 
+58
objective-c core-animation
Jul 17 '12 at 3:30
source share
3 answers

When you add animation to a layer, the animation does not change the properties of the layer. Instead, the system creates a copy of the layer. The initial layer is called the model layer, and the duplicate is called the presentation layer. The properties of the presentation layer change as the animation progresses, but the properties of the model layer remain unchanged.

When you remove an animation, the system destroys the presentation layer, leaving only the model layer, and then the model layer properties control how the layer is drawn. Therefore, if the properties of the model layer do not correspond to the final animated values โ€‹โ€‹of the properties of the view layer, the layer will be instantly reset until it appears before the animation.

To fix this, you need to set the model layer properties to the final animation values, and then add the animation to the layer. You want to do this in this order, because changing a layer property can add implicit animation to the property, which contradicts the animation you want to explicitly add. You want your explicit animation to overlap the implicit animation.

So how do you do all this? The basic recipe is as follows:

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fromValue = [NSValue valueWithCGPoint:myLayer.position]; layer.position = newPosition; // HERE I UPDATE THE MODEL LAYER PROPERTY animation.toValue = [NSValue valueWithCGPoint:myLayer.position]; animation.duration = .5; [myLayer addAnimation:animation forKey:animation.keyPath]; 

I did not use an animation group, so I do not know exactly what you need to change. I just add each animation separately to the layer.

Itโ€™s also easier for me to use the +[CATransaction setCompletionBlock:] method to set a completion handler for one or more animations instead of trying to use an animation delegate. You install the transaction completion block, then add the animation:

 [CATransaction begin]; { [CATransaction setCompletionBlock:^{ [self.imageView removeFromSuperview]; }]; [self addPositionAnimation]; [self addScaleAnimation]; [self addOpacityAnimation]; } [CATransaction commit]; 
+179
Jul 17 '12 at 3:45
source share

CAAnimations are automatically deleted upon completion. There is a removedOnCompletion property that controls this. You must set the value to NO .

In addition, there is something known as fillMode that controls the behavior of an animation before and after its duration. This property is declared on CAMediaTiming (which corresponds to CAAnimation ). You must set the value of kCAFillModeForwards .

With both of these changes, the animation should persist after it is completed. However, I do not know whether to change them in a group or in separate animations within a group or both.

+32
Jul 17 '12 at 3:45
source share

Here is an example in Swift that might help someone

This is a gradient level animation. It animates the .locations property.

The critical point as @robMayoff's answer fully explains that:

Surprisingly, when you do an animation of a layer, you actually set the final value before starting the animation!

The following is a good example as the animation repeats endlessly.

When an animation repeats endlessly, you will sometimes see a โ€œflashโ€ between animations if you make the classic mistake of โ€œforgetting to set a value before reviving it!โ€

 var previousLocations: [NSNumber] = [] ... func flexTheColors() { // "flex" the color bands randomly let oldValues = previousTargetLocations let newValues = randomLocations() previousTargetLocations = newValues // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!" theLayer.locations = newValues // AND NOW ANIMATE: CATransaction.begin() // and by the way, this is how you endlessly animate: CATransaction.setCompletionBlock{ [weak self] in if self == nil { return } self?.animeFlexColorsEndless() } let a = CABasicAnimation(keyPath: "locations") a.isCumulative = false a.autoreverses = false a.isRemovedOnCompletion = true a.repeatCount = 0 a.fromValue = oldValues a.toValue = newValues a.duration = (2.0...4.0).random() theLayer.add(a, forKey: nil) CATransaction.commit() } 

The following may help clarify something for new programmers. Please note that in my code I do this:

  // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!" theLayer.locations = newValues // AND NOW ANIMATE: CATransaction.begin() ...set up the animation... CATransaction.commit() 

however, in the code example in another answer, it looks like this:

  CATransaction.begin() ...set up the animation... // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!" theLayer.locations = newValues CATransaction.commit() 

Regarding the position of the line of code where you "set the values, before the animation!" ..

Itโ€™s actually great that this line actually โ€œinsideโ€ starts with lines of code. While you do this until .commit() .

I only mention this, as this can confuse new animators.

+1
Dec 10 '17 at 18:40
source share



All Articles