Change CAShapeLayer without Animation

2019-04-05 01:58发布

问题:

I want to set the strokeEnd property of a CAShapeLayer without the default animation, with no animation at all. I've looked around to try to find how to do this but everything seems to be about how to animate properties.

回答1:

In Core Animations terminology, a more general term for an animation is an "action". You can for example see that the CAAnimation conforms to the CAAction protocol. You also see the terminology "action" used when disabling them (disabling the animations).

There are many different ways of changing the actions of a layer. Many of them are documented pretty well in the discussion of the actionForKey: documentation on CALayer (excerpt below). Some of them are more relevant when subclassing (and you can also override actionForKey: in your subclass to add more implicit actions for new keys.

This method searches for the layer’s associated actions in the following order:

  1. If the layer has a delegate and that delegate implements the Accessing the Layer’s Filters method, the layer calls that method. The delegate must do one of the following:
    • Return the action object for the given key.
    • Return nil if it does not handle the action.
    • Return the NSNull object if it does not handle the action and the search should be terminated.
  2. The layer looks in the layer’s actions dictionary.
  3. The layer looks in the `style dictionary for an actions dictionary that contains the key.
  4. The layer calls its `defaultActionForKey: method to look for any class-defined actions.
  5. The layer looks for any implicit actions defined by Core Animation.

The two ways that are most interesting when you want to disable animation are (two different because they are used for slightly different things):

  1. Disabling actions with a CATransaction (not mentioned above)
  2. Setting [NSNull null] for the @"strokeEnd" key in the actions dictionary (number 2 above)

Disabling actions

Using a transaction to disable animations is good when you temporarily want to disable actions completely for a number of different properties while still having animations everywhere else. In code it looks something like this:

[CATransaction begin];
[CATransaction setDisableActions:YES];
// change your property here 
yourShapeLayer.strokeEnd = 0.7;
[CATransaction commit]; // animations are disabled until here...

Changing the actions dictionary

You can permanently change the default animation for one or more keys by modifying the layers action dictionary. Setting [NSNull null] means that there should be no animation and that the layer should stop looking elsewhere for a default animation. You can also use this to add animatable properties. Removing an animation using the action dictionary looks something like this:

yourShapeLayer.actions = @{@"strokeEnd": [NSNull null]};
yourShapeLayer.strokeEnd = 0.7; // this won't have an animation


回答2:

You can do something like the following:

NSDictionary *actions = @{@"strokeEnd": [NSNull null]};
yourShapeLayer.actions = actions;

This will make it so it won't animate for the property. You could also specify additional properties to not animate by adding them to this:

NSDictionary *actions = @{@"strokeEnd": [NSNull null], @"position": [NSNull null], @"position": [NSNull null]};


回答3:

I think a nice solution would be to create a CABasicAnimation(keyPath: "strokeEnd") and set the duration to something really small. Here is an example:

let basicAnimation = CABasicAnimation(keyPath: "strokeEnd")
basicAnimation.toValue = someValue
basicAnimation.duration = animated ? 2.0 : 0.01
basicAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)

basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false

// Apply the animation
circleProgress.add(basicAnimation, forKey: "progress")