Set UITableViewCell background color using a CALay

2019-06-05 04:23发布

问题:

On iOS, I am adding a CALayer to a UITableViewCell's layer. This is my first time using CALayer, and it is simply supposed to change the background color of the table cell. My goal is (1) to learn how to use CALayer, and (2) to test using Instruments whether the drawing is faster than my current implementation, which slows down on CGContextFillRect.

(Technical Q&A QA1708 was the catalyst for all this.)

Current Implementation (works)

- (void)drawRect:(CGRect)r
{
    UIColor *myColor = [self someColor];
    [myColor set];
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextFillRect(context, r);  // draw the background color
    // now draw everything else
    // [...]

}

Attempted New Implementation (doesn't work)

#import <QuartzCore/QuartzCore.h>

@implementation MyCell {
    CALayer *backgroundLayer;
}

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

    if (self) {
        // [...other stuff here too]
        backgroundLayer = [[CALayer alloc] init];
        [[self layer] addSublayer:backgroundLayer];
    }

    return self;
}

- (void)drawRect:(CGRect)r {
    backgroundLayer.frame = CGRectMake(0, 0, r.size.width, r.size.height);
    [backgroundLayer setBackgroundColor:[self someColor]];
    // now draw everything else
    // [...]
}

I see the correct colors, but none of the other drawing (I'm assuming the custom drawing ends up behind my new layer).

If I remove the backgroundLayer.frame = ... line, all of my other drawing is still there, but on a black background.

What am I missing?

回答1:

The reason why you're getting unexpected behavior is because of UITableViewCell's relatively complex view hierarchy:

- UITableViewCell
   - contentView
   - backgroundView
   - selectedBackgroundView

Whenever you define custom drawing routines in a UITableViewCell, you should be doing so within the contentView hierarchy. This involves subclassing UIView, overriding -drawRect:, and adding it as a subview into the contentView.

The reason why your background color was being ignored in your example was due to your adding your CALayer as a sublayer of the UITableViewCell's layer. This is obscured by the UITableViewCell's contentView.

However, for some reason, you wish to use a CALayer here. I'd like to understand why as it doesn't have anything that a UIView doesn't have. You can set the backgroundColor on your contentView instead of doing this roundabout set of things.

Here's an example using CALayer as you requested:

@implementation JRTableViewCell

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

- (void)addCustomLayerToContentView {
   CALayer *layer = [[CALayer alloc] initWithFrame:[self bounds]];  
   [layer setBackgroundColor:[UIColor blueColor]]; //use whatever color you wish.

   [self.contentView.layer addSublayer:layer];
}

@end