简体   繁体   中英

CALayer: Where does the implicit animation comes from?

I'm new to Core Animation. While learning about implicit animations, I came across a question.

I put a layer on the screen and made it color change to a random value when pressing a button, which triggers an implicit animation.

#define RANDOM_0_1 (arc4random() / (CGFloat)UINT_MAX)

- (IBAction)changeColor:(id)sender {
    CGFloat r = RANDOM_0_1;
    CGFloat g = RANDOM_0_1;
    CGFloat b = RANDOM_0_1;
    self.colorLayer.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:1.0f].CGColor;
}

It works pretty well. Then I use

NSLog(@"%@", [self.colorLayer actionForKey:@"backgroundColor"]);

to get the animation object passed implicitly to the layer. I got

<CABasicAnimation: 0x7f8ae1d21970>.

Referring to the document , I learned that there are four way for a layer to get an action. 'Delegate, actions dictionary, style dictionary and +[CALayer defaultActionForKey:]' Then I stated wonder which step does the animation object really comes from. So I wrote this to check

NSLog(@"%@ %@ %@", self.colorLayer.delegate, self.colorLayer.actions, self.colorLayer.style);

It gave me three (null)

 (null) (null) (null)

As the document said, these values should be set to nil by default.

So it must be +[CALayer defaultActionForKey:] that gives me the animation object. However, when I call

NSLog(@"%@", [self.colorLayer actionForKey:@"backgroundColor"]);

it still gave me a (null).

That's quite strange I thought. I started wonder if the 'key' passed into has been somehow changed by the internal implementation. So I referred to this post to print the argument passed to the method as well as the return value.

static id<CAAction> (*__originalCALayerDefaultActionForKey)( CALayer *, SEL, NSString *) ;
static id<CAAction> CALayerDefaultActionForKey( CALayer * self, SEL _cmd, NSString * event )
{
    id res = (*__originalCALayerDefaultActionForKey)( self, _cmd, event );
    NSLog(@"%@<%p> %@ %@\n", [ self class ], self, event, res ) ;
    return res;
}

I got the result like this

CALayer<0x106da5ef0> position (null)
CALayer<0x106da5ef0> bounds (null)
CALayer<0x106da5ef0> backgroundColor (null)

the 'key' passed into was exactly the property name, but it always returns null.

So, could anyone explain where does the animation object comes on earth, or provide me some technique to find out the answer?

Thanks. :)

The CALayer's SDK default animations are implemented in the .m, which you cannot get it in normal way.

And the SDK offers optional ways for user to use customised animation:

delegate :

You can use a delegate object to provide the layer's contents, handle the layout of any sublayers, and provide custom actions in response to layer-related changes. The object you assign to this property should implement one or more of the methods of the CALayerDelegate informal protocol. ...

actions :

The default value of this property is nil. You can use this dictionary to store custom actions for your layer. The contents of this dictionary searched as part of the standard implementation of the actionForKey: method.

they're just for animation customisation.

eg you can create a custom CALayer subclass (say CustomLayer) which override the -actionForKey :

- (id<CAAction>)actionForKey:(NSString *)event
{
  if ([event isEqualToString:@"strokeStart"] || [event isEqualToString:@"strokeEnd"]) {
    CABasicAnimation * strokAnimation = [CABasicAnimation animationWithKeyPath:event];
    strokAnimation.removedOnCompletion = NO;
    strokAnimation.fillMode = kCAFillModeForwards;
    strokAnimation.duration = .3f;
    strokAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    strokAnimation.fromValue = [self.presentationLayer valueForKey:event];
    return strokAnimation;
  }
  return [super actionForKey:event];
}

in this way, whenever you update strokStart & strokend for your CustomLayer instance, it'll invoke the strokAnimation you offered instead of the default animation that SDK implemented.


But style , it's for setting the animatable properties' value w/o animation (cause by default, whenever you update CALayer instance's animatable properties' value, it'll invoke default animation if you don't offer custom one):

An optional dictionary used to store property values that aren't explicitly defined by the layer.

If the style dictionary does not define a value for an attribute, the receiver's defaultValueForKey: method is called. The default value of this property is nil.

eg like strokeStart & strokeEnd , their default values are 0.f & 1.f , you can modify style to use customized default value, like:

aLayer.style = @{@"strokeStart" : @.2f,
                 @"strokeEnd"   : @.6f};

in this way, the layer's animation won't be invoked, like in the initialisation step, especially when you've offered a complex animation for the key, it'll helps a lot.


For +defaultActionForKey: , it's more like user defined default actions, as the doc said:

Returns a suitable action object for the given key or nil of no action object was associated with that key.

Classes that want to provide default actions can override this method and use it to return those actions.

You can override this method like -actionForKey: , below is the order to check whether the action exists when you modified the related property:

  1. Check whether the delegate exists & delegate method -actionForLayer:forKey: is implemented;
  2. Check whether the action of key was offered in -actionForKey: ;
  3. Check whether the action of key exists in actions while invoking [super actionForKey:event] after step 2;
  4. Follows step 3 if no action of key exists in actions , check whether the action of key exists in +defaultActionForKey: .
  5. Use SDK implemented default actions, which we cannot get (or might have unknown event name), as far as I know.

Generally, +defaultActionForKey: is like a global customised setting in class level for your CALayer subclass, while actions , -actionForLayer:forKey: & -actionForKey: to handle special layers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM