I have a UIView whose layers will have sublayers. I'd like to assign delegates for each of those sublayers, so the delegate method can tell the layer what to draw. My question is:
What should I provide as CALayer's delegate? The documentation says not to use the UIView the layers reside in, as this is reserved for the main CALayer of the view. But, creating another class just to be the delegate of the CALayers I create defeats the purpose of not subclassing CALayer. What are people typically using as the delegate for CALayer? Or should I just subclass?
Also, why is it that the class implementing the delegate methods doesn't have to conform to some sort of CALayer protocol? That's a wider overarching question I don't quite understand. I thought all classes requiring implementation of delegate methods required a protocol specification for implementers to conform to.
Preferring to keep the layer delegate methods in my UIView subclass, I use a basic re-delegating delegate class. This class can be reused without customization, avoiding the need to subclass CALayer or create a separate delegate class just for layer drawing.
@interface LayerDelegate : NSObject
- (id)initWithView:(UIView *)view;
@end
with this implementation:
@interface LayerDelegate ()
@property (nonatomic, weak) UIView *view;
@end
@implementation LayerDelegate
- (id)initWithView:(UIView *)view {
self = [super init];
if (self != nil) {
_view = view;
}
return self;
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
NSString *methodName = [NSString stringWithFormat:@"draw%@Layer:inContext:", layer.name];
SEL selector = NSSelectorFromString(methodName);
if ([self.view respondsToSelector:selector] == NO) {
selector = @selector(drawLayer:inContext:);
}
void (*drawLayer)(UIView *, SEL, CALayer *, CGContextRef) = (__typeof__(drawLayer))objc_msgSend;
drawLayer(self.view, selector, layer, context);
}
@end
The layer name is used to allow for per-layer custom draw methods. For example, if you have assigned a name to your layer, say layer.name = @"Background";
, then you can implement a method like this:
- (void)drawBackgroundLayer:(CALayer *)layer inContext:(CGContextRef)context;
Note, your view will need a strong reference the instance of this class, and it can be used as the delegate for any number of layers.
layerDelegate = [[LayerDelegate alloc] initWithView:self];
layer1.delegate = layerDelegate;
layer2.delegate = layerDelegate;
The lightest-wight solution would be to create a small helper class in the the file as the UIView that's using the CALayer:
In MyView.h
@interface MyLayerDelegate : NSObject
. . .
@end
In MyView.m
@implementation MyLayerDelegate
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx
{
. . .
}
@end
Just place those at the top of your file, immediately below the #import directives. That way it feels more like using a "private class" to handle the drawing (although it isn't -- the delegate class can be instantiated by any code that imports the header).
Take a look at the docs on formal vs informal protocols. The CALayer is implementing an informal protocol which means that you can set any object to be its delegate and it will determine if it can send messages to that delegate by checking the delegate for a particular selector (i.e. -respondsToSelector).
I typically use my view controller as the delegate for the layer in question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With