How to Disable Calayer Implicit Animations

Disabling implicit animations in -[CALayer setNeedsDisplayInRect:]

You can do this by setting the actions dictionary on the layer to return [NSNull null] as an animation for the appropriate key. For example, I use

NSDictionary *newActions = @{
@"onOrderIn": [NSNull null],
@"onOrderOut": [NSNull null],
@"sublayers": [NSNull null],
@"contents": [NSNull null],
@"bounds": [NSNull null]
};

layer.actions = newActions;

to disable fade in / out animations on insertion or change of sublayers within one of my layers, as well as changes in the size and contents of the layer. I believe the contents key is the one you're looking for in order to prevent the crossfade on updated drawing.


Swift version:

let newActions = [
"onOrderIn": NSNull(),
"onOrderOut": NSNull(),
"sublayers": NSNull(),
"contents": NSNull(),
"bounds": NSNull(),
]

How to prevent CALayer from implicit animations?

You can wrap the change in a CATransaction with disabled animations:

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
//change background colour
[CATransaction commit];

CATextLayer - how to disable implicit animations?

The problem is that CATransaction.disableActions() does not do what you think it does. You need to say CATransaction.setDisableActions(true).

And then you can get rid of all the other stuff you're saying, as it is pointless. This code alone is sufficient to change the color without animation:

CATransaction.setDisableActions(true)
layer.foregroundColor = UIColor.redColor().CGColor // or whatever

(You can wrap it in a begin()/commit() block if you have some other reason to do so, but there is no need just in order to switch off implicit layer property animation.)

Disable CALayer mask frame change animations

Just call layer removeAllAnimations() AFTER setting the frame.

class GradientView: UIView {

private(set) var gradientLayer: CAGradientLayer

override init(frame: CGRect) {
gradientLayer = CAGradientLayer()
super.init(frame: frame)
layer.insertSublayer(gradientLayer, at: 0)
}

required init?(coder aDecoder: NSCoder) {
fatalError("not implemented")
}

override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
gradientLayer.removeAllAnimations() // remove implicit animation from frame change
}

}

How To Disable Implicit or Automatic Animations

Alright, many headaches and much wasted time later, I figured out what the heck was going on in my code. I thought I was experiencing implicit animations, but I couldn't figure out why this started happening all of the sudden.

I decided I better try to understand implicit animations, so I experimented on my own to figure out how to achieve them in a controlled situation. The reason I have never seen implicit animations happen is because I am always using UIView or one of its subclasses.

I learned that if you start with a CALayer and work strictly with the layer, all changes to many of the properties will implicitly animate.

There may be some confusion (I know there was for me) when you see that UIView's (and their posterity) all are automatically layer backed and have a CALayer property.

Never-the-less, it is evident that UIView is somehow overriding the implicit animation mechanism of its CALayer property. So if you want implicit animations, you must use CALayer's directly, not just suppose that because UIView has a CALayer property that it will behave the same.

As for the bug I was experiencing...It was perhaps the strangest one I have yet come across. Everything, no matter what I tried, was animating any changes to values without any animation code. The culprit ended up being a nested UIView animation block.

Notice the following and see if you catch the issue right off:

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.2];
//animate something
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelay:.8];
[UIView setAnimationDuration:.4];
//animate something else
[UIView commitAnimations];

I failed to terminate the nested block with another [UIView commitAnimations]. It was literally leaking animation in my program. Everything was animating, even code in completely different classes. This bug is squashed...on to the next!

CATextLayer - how to disable implicit animations?

The problem is that CATransaction.disableActions() does not do what you think it does. You need to say CATransaction.setDisableActions(true).

And then you can get rid of all the other stuff you're saying, as it is pointless. This code alone is sufficient to change the color without animation:

CATransaction.setDisableActions(true)
layer.foregroundColor = UIColor.redColor().CGColor // or whatever

(You can wrap it in a begin()/commit() block if you have some other reason to do so, but there is no need just in order to switch off implicit layer property animation.)

Disable Implicit Animation of CATextLayer.string Property

Figured it out, using the answer for this question: Disabling implicit animations in -[CALayer setNeedsDisplayInRect:]

In my particular case, to stop the changing of the CATextLayer.string property from being animated, this code was enough:

NSDictionary *newActions = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNull null], @"contents", nil];
textLayer.actions = newActions;
[newActions release];

In other words, it seems that the contents key disables animations on changes to the CATextLayer.string property.

Selectively overriding CALayer implicit animations

Your subclass is going to get actionForKey: called on it for every key, so if you return nil, there won't be an action for that key. If you want the default animations, you should return either [super actionForKey:event] or [CALayer defaultActionForKey:event].

A layer's frame is calculated from its bounds and position, so if there's no animation for those two, there can't be an animation for frame.*

What you want to do is:

[CATransaction setValue:kCFBooleanTrue
forKey:kCATransactionDisableActions]

when your layer resizes as a result of the window resizing. This will disable animation just this time (for this transaction); when you change the layer's size directly, it will still animate. I'm not sure what the correct spot to do that call is; I just tried doing it in a callback from notification that the window was resizing, but that didn't work. I'm sure it won't be too hard for you to figure out where to put it in your code.


*: In fact, the docs say:

Note: The frame property is not directly animatable. Instead you should animate the appropriate combination of the bounds, anchorPoint and position properties to achieve the desired result.



Related Topics



Leave a reply



Submit