0
votes

I'm using several CALayers to draw my content in a UITableViewCell subclass. The actual drawing is supposed to happen by calling the CALayer delegate method drawLayer:inContext:. However the delegate methods never gets called.

Here is my structure:

  • CustomCell: UITalbeViewCell
    • creates CustomCellContentView: UIView
      • creates CABackgroundLayer: CALayer
      • creates CATextLayer: CALayer
    • creates CALayerDelegate: NSObject

Each customCell has a customContentView, which holds all the CALayers for drawing. Each customCell creates a caLayerDelegate for the CALayers to call. The caLayerDelegate reports back to the customCell.

CustomCell:

#define CALAYER_TEXTLAYER_NAME              @"CATextLayer"
#define CALAYER_BGLAYER_NAME                @"CABackgroundLayer"

...

- (id) customContentViewForCurrentCell
{
    CGRect contentViewFrame = CGRectMake(-CC_LEFTRIGHT_PADDING, 0., self.contentView.bounds.size.width, self.contentView.bounds.size.height);

    CustomCellContentView *cellContentView = [[[CustomCellContentView alloc] initWithFrame: contentViewFrame] autorelease];

    CALayerDelegate *layerDelegate = [[CALayerDelegate alloc] initWithView: self];
    cellContentView.layerDelegate = layerDelegate;
    [cellContentView setupSubLayers];

    [layerDelegate release];

    return cellContentView;
}

#pragma mark - Initialisation

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (!self) 
        return nil;

    self.customContentView = (CustomCellContentView *)[self customContentViewForCurrentCell];
    self.customContentView.opaque = TRUE;

    self.backgroundColor = [UIColor clearColor];
    self.selectionStyle = UITableViewCellSelectionStyleNone;
    [self.contentView addSubview: self.customContentView];

    return self;
}

#pragma mark - Cell Update

- (void) refreshLayout
{
    [self setNeedsDisplay];
    [self.customContentView setNeedsDisplay];
}

...

#pragma mark - CALayer Delegate Methods

-(void) drawLayer: (CALayer*) layer inContext: (CGContextRef) context 
{

}

-(void) drawCABackgroundLayer: (CALayer*) layer inContext: (CGContextRef) context
{
    UIGraphicsPushContext(context);

    CGRect contentRect = [layer bounds];
    UIImage *bgImage = [[ImageCacheController sharedImageCache] imageFromCache: GENERIC_BGIMAGE_FILENAME];

    [bgImage drawInRect: CGRectMake(contentRect.origin.x, contentRect.origin.y, contentRect.size.width, contentRect.size.height)];

    UIGraphicsPopContext();
}

-(void) drawCATextLayer: (CALayer*) layer inContext: (CGContextRef) context
{
    UIGraphicsPushContext(context);

    CGContextSetShadowWithColor(context, CGSizeMake(0.f, 2.0f), 0.0f, [UIColor mainLabelShadowColor].CGColor);

    [[UIColor mainLabelColor] set];
    UIFont *mainFont = [UIFont fontWithName: FONT_INFOS size: CC_MAINLABEL_FONTSIZE_MAX];

    [@"ArthurDent" drawAtPoint: [self mainViewPosition] 
                           forWidth: CC_MAINLABEL_WIDTH 
                           withFont: mainFont 
                        minFontSize: CC_MAINLABEL_FONTSIZE_MAX 
                     actualFontSize: NULL 
                      lineBreakMode: UILineBreakModeTailTruncation 
                 baselineAdjustment: UIBaselineAdjustmentAlignBaselines];

    UIGraphicsPopContext();
}

CustomCellContentView:

@interface CustomCellContentView : UIView 
{
    CALayerDelegate *layerDelegate;

    CALayer *backgroundLayer;
    CALayer *textLayer;
}

@property (nonatomic, retain) CALayerDelegate *layerDelegate;

@property (nonatomic, retain) CALayer *backgroundLayer;
@property (nonatomic, retain) CALayer *textLayer;

- (void) setupSubLayers;

@end



@implementation CustomCellContentView

@synthesize textLayer, backgroundLayer, layerDelegate;

#pragma mark - Initialisation

- (id) initWithFrame:(CGRect)frame
{
    self = [super initWithFrame: frame];

    if(!self)
        return nil;

    return self;
}

- (void) setupSubLayers
{    
    self.textLayer = [CALayer layer];
    self.textLayer.name = CALAYER_TEXTLAYER_NAME;
    self.textLayer.shadowColor = [UIColor mainLabelShadowColor].CGColor;
    self.textLayer.shadowOffset = CGSizeMake(0.0f, 2.0f);
    self.textLayer.shadowOpacity = 1.f;
    self.textLayer.shadowRadius = 0.f;
    self.textLayer.needsDisplayOnBoundsChange = TRUE;
    self.textLayer.delegate = self.layerDelegate;

    self.backgroundLayer = [CALayer layer];
    self.backgroundLayer.name = CALAYER_BGLAYER_NAME;
    self.backgroundLayer.needsDisplayOnBoundsChange = TRUE;
    self.backgroundLayer.delegate = self.layerDelegate;

    [self.layer addSublayer: self.textLayer];
    [self.layer addSublayer: self.backgroundLayer];

    [self.textLayer setNeedsDisplay];
    [self.backgroundLayer setNeedsDisplay];
}

#pragma mark - SetNeedsDisplay Overwritten

- (void) setNeedsDisplay
{
    [super setNeedsDisplay];

    [self.layer setNeedsDisplay];    
    [self.textLayer setNeedsDisplay];
    [self.backgroundLayer setNeedsDisplay];
}

...

CALayerDelegate:

@implementation CALayerDelegate

#pragma mark - Initialisation

-(id) initWithView: (UIView *) view 
{
    self = [super init];

    if (!self) 
        return nil;

    _view = view;

    return self;
}

#pragma mark - CALayer Delegate Methods

-(void) drawLayer: (CALayer*) layer inContext: (CGContextRef) context 
{
    NSString* methodName = [NSString stringWithFormat: @"draw%@:inContext:", [layer name]];

    SEL selector = NSSelectorFromString(methodName);

    if (![_view respondsToSelector: selector])
        selector = @selector(drawLayer:inContext:);

    [_view performSelector: selector 
                withObject: layer 
                withObject: (id)context];
}

@end

I've read for UIViewController it is better to create sub-CALayers in awakeFromNib: and not in initWithFrame:, because there is some form of layer - backing happening after the initWithFrame:, which will discard the creation of the sublayers. So, you should only add sublayers after the view is loaded?

I use seperate CALayerDelegate - objects, so the CustomCell and its view are only responsible for the customContentView.layer.

Anyone knows, what I'm doing wrong?

Thanks in advance!!

2
Is it possible your delegate class's drawLayer:inContext: method is being called and the delegate class is failing to forward the message to your view? Or is your problem that drawLayer:inContext: is not being called all? It's not clear from your description. - Robin Summerhill

2 Answers

0
votes

From the documentation for CALayer describing the 'delegate' property: 'In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer'.

If I read that correctly then you need to set CustomCellContentView as the layer delegate. Move the functionality from CALayerDelegate directly into your custom view class and see if that works. That would be the standard way to do it anyway, rather than creating a separate class just to act as a delegate.

0
votes

Check that your frame is not CGRectZero.

I had a similar issue to what you describe. If the frame is CGRectZero this will prevent the drawLayer:inContext from being called.