Properties on CALayer subclass aren't getting

2019-04-01 00:52发布

问题:

I have a subclass of CALayer with a custom property, declared as such:

@interface MyLayer : CALayer
    @property (nonatomic,retain) NSNumber *customValue;
@end
@implementation MyLayer
    @synthesize customValue = _customValue;
@end

I want to animate this property inside of an explicit CATranasction, so i set up a delegate with the actionForLayer:forKey: method implemented which returns an animation, however any changes to someMyLayerInstance.customValue inside of [CATransaction begin] ... [CATransaction end] do not result in actionForLayer:forKey getting called with a corresponding 'key' value.

However, nuking the property on MyLayer and making changes to myLayerInstance by calling setValue:forKey: does result in actionForLayer:forKey: getting called.

It appears that this is because CALayer does some mojo for key/value coding for undefined properties. How can I recreate this mojo so that I can declare properties on MyLayer, but still have them be observed by the animation delegate?

回答1:

The most important thing is that you need to implement all CALayer accessors using @dynamic. Do not use @synthesize and do not implement the accessors directly. CALayer generates all its own property handlers (as you've indirectly discovered), and you need to let those be used.

You also need to let CALayer know that this property is display-impacting (which you may have already done given your other comments). If you haven't, you do this by implementing +needsDisplayForKey: and returning YES for your key.

Here's an example of a CALayer that animates a custom property (taken from Chapter 7 of iOS 5 Programming Pushing the Limits. The full sample code is available at the Wiley site.) This example implements actionForKey: in the layer, but you can still implement that part in the delegate if you prefer.

@implementation CircleLayer
@dynamic radius;

...

+ (BOOL)needsDisplayForKey:(NSString *)key {
  if ([key isEqualToString:@"radius"]) {
    return YES;
  }
  return [super needsDisplayForKey:key];
}

- (id < CAAction >)actionForKey:(NSString *)key {
  if ([self presentationLayer] != nil) {
    if ([key isEqualToString:@"radius"]) {
      CABasicAnimation *anim = [CABasicAnimation
                                animationWithKeyPath:@"radius"];
      anim.fromValue = [[self presentationLayer] 
                        valueForKey:@"radius"];
      return anim;
    }
  }

  return [super actionForKey:key];
}

@end