Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How exactly to subclass CALayer and use a custom property?

I am trying to create a subclass of CALayer with a custom index property that I can both animate and change directly in order to display a different picture based on the index.

In the header, I declared:

@property NSUInteger index;

In the implementation, I overrode needDisplayForKey:

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

Now, in the display method, I want to display a picture based on the index. However, during an animation, the value of self.index never changes, so I have to query the value of the presentation layer, contrary to the example on Providing CALayer Content by Subclassing:

- (void)display
{
    NSUInteger presentationIndex = [(CustomLayer *)[self presentationLayer] index];
    self.contents = (id)[[images objectAtIndex:presentationIndex] CGImage];
}

The problem is, if I do that, I cannot set the index value directly outside of an animation, because it will only change the model layer, and the display method explicitly queries the presentation layer.

If I add an initialization method that copies the value of index, it works:

- (id)initWithLayer:(id)layer
{
    self = [super initWithLayer:layer];
    if (self) {
        if ([layer isKindOfClass:[CustomLayer class]])
            self.index = [(CustomLayer *)layer index];
    }
    return self;
}

However, after or before an animation, there is always a 1 image glitch because the presentation or the model value don't match (depending if I set index to the destination value or not).

  • Surprisingly, it seems like the drawInContext: method always has the right value for [self index], but it is not the method I want to use since I just set the content property with an image.
  • I get different behaviors depending on the way I implement the index property. If I use @dynamic index (which works, even though the documentation doesn't say that custom property getters/setters would be dynamically implemented), display is called every time the value of index is changed. If I use @synthesize or implement a setter, display is not called, so I would need to change content in the setter too.
  • Should I use an instance variable? Should I use the dynamic implementation? Or should I use setValue:forKey: instead?

As you can see, I am a bit confused about how to get the result I want, and how to correctly implement a subclass of CALayer with a custom property. Any help, and explanations, would be appreciated!

like image 287
Guillaume Avatar asked Apr 30 '12 16:04

Guillaume


1 Answers

There is an excellent tutorial on the web that explains how to create a custom CALayer subclass with animatable properties. I haven't had occasion to try it yet, but I bookmarked it. Here is the link:

Animating Pie Slices Using a Custom CALayer

It looks like the main tricks are:

Use @dynamic, not @synthesize for the animatable properties of your custom CALayer.

Override actionForKey:, initWithLayer:, and needsDisplayForKey:, and perhaps drawInContext:

like image 91
Duncan C Avatar answered Oct 23 '22 12:10

Duncan C