[ios] How to customize the background/border colors of a grouped table view cell?

I would like to customize both the background and the border color of a grouped-style UITableView.

I was able to customize the background color by using the following:

tableView.contentView.backgroundColor = [UIColor greenColor];

But the border color is still something I don't know how to change.

How do I customize these two aspects of the grouped-style table view?

This question is related to ios objective-c iphone uitableview cocoa-touch

The answer is


UPDATE: In iPhone OS 3.0 and later UITableViewCell now has a backgroundColor property that makes this really easy (especially in combination with the [UIColor colorWithPatternImage:] initializer). But I'll leave the 2.0 version of the answer here for anyone that needs it…


It's harder than it really should be. Here's how I did this when I had to do it:

You need to set the UITableViewCell's backgroundView property to a custom UIView that draws the border and background itself in the appropriate colors. This view needs to be able to draw the borders in 4 different modes, rounded on the top for the first cell in a section, rounded on the bottom for the last cell in a section, no rounded corners for cells in the middle of a section, and rounded on all 4 corners for sections that contain one cell.

Unfortunately I couldn't figure out how to have this mode set automatically, so I had to set it in the UITableViewDataSource's -cellForRowAtIndexPath method.

It's a real PITA but I've confirmed with Apple engineers that this is currently the only way.

Update Here's the code for that custom bg view. There's a drawing bug that makes the rounded corners look a little funny, but we moved to a different design and scrapped the custom backgrounds before I had a chance to fix it. Still this will probably be very helpful for you:

//
//  CustomCellBackgroundView.h
//
//  Created by Mike Akers on 11/21/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import <UIKit/UIKit.h>

typedef enum  {
    CustomCellBackgroundViewPositionTop, 
    CustomCellBackgroundViewPositionMiddle, 
    CustomCellBackgroundViewPositionBottom,
    CustomCellBackgroundViewPositionSingle
} CustomCellBackgroundViewPosition;

@interface CustomCellBackgroundView : UIView {
    UIColor *borderColor;
    UIColor *fillColor;
    CustomCellBackgroundViewPosition position;
}

    @property(nonatomic, retain) UIColor *borderColor, *fillColor;
    @property(nonatomic) CustomCellBackgroundViewPosition position;
@end

//
//  CustomCellBackgroundView.m
//
//  Created by Mike Akers on 11/21/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
//

#import "CustomCellBackgroundView.h"

static void addRoundedRectToPath(CGContextRef context, CGRect rect,
                                 float ovalWidth,float ovalHeight);

@implementation CustomCellBackgroundView
@synthesize borderColor, fillColor, position;

- (BOOL) isOpaque {
    return NO;
}

- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        // Initialization code
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    // Drawing code
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(c, [fillColor CGColor]);
    CGContextSetStrokeColorWithColor(c, [borderColor CGColor]);

    if (position == CustomCellBackgroundViewPositionTop) {
        CGContextFillRect(c, CGRectMake(0.0f, rect.size.height - 10.0f, rect.size.width, 10.0f));
        CGContextBeginPath(c);
        CGContextMoveToPoint(c, 0.0f, rect.size.height - 10.0f);
        CGContextAddLineToPoint(c, 0.0f, rect.size.height);
        CGContextAddLineToPoint(c, rect.size.width, rect.size.height);
        CGContextAddLineToPoint(c, rect.size.width, rect.size.height - 10.0f);
        CGContextStrokePath(c);
        CGContextClipToRect(c, CGRectMake(0.0f, 0.0f, rect.size.width, rect.size.height - 10.0f));
    } else if (position == CustomCellBackgroundViewPositionBottom) {
        CGContextFillRect(c, CGRectMake(0.0f, 0.0f, rect.size.width, 10.0f));
        CGContextBeginPath(c);
        CGContextMoveToPoint(c, 0.0f, 10.0f);
        CGContextAddLineToPoint(c, 0.0f, 0.0f);
        CGContextStrokePath(c);
        CGContextBeginPath(c);
        CGContextMoveToPoint(c, rect.size.width, 0.0f);
        CGContextAddLineToPoint(c, rect.size.width, 10.0f);
        CGContextStrokePath(c);
        CGContextClipToRect(c, CGRectMake(0.0f, 10.0f, rect.size.width, rect.size.height));
    } else if (position == CustomCellBackgroundViewPositionMiddle) {
        CGContextFillRect(c, rect);
        CGContextBeginPath(c);
        CGContextMoveToPoint(c, 0.0f, 0.0f);
        CGContextAddLineToPoint(c, 0.0f, rect.size.height);
        CGContextAddLineToPoint(c, rect.size.width, rect.size.height);
        CGContextAddLineToPoint(c, rect.size.width, 0.0f);
        CGContextStrokePath(c);
        return; // no need to bother drawing rounded corners, so we return
    }

    // At this point the clip rect is set to only draw the appropriate
    // corners, so we fill and stroke a rounded rect taking the entire rect

    CGContextBeginPath(c);
    addRoundedRectToPath(c, rect, 10.0f, 10.0f);
    CGContextFillPath(c);  

    CGContextSetLineWidth(c, 1);  
    CGContextBeginPath(c);
    addRoundedRectToPath(c, rect, 10.0f, 10.0f);  
    CGContextStrokePath(c); 
}


- (void)dealloc {
    [borderColor release];
    [fillColor release];
    [super dealloc];
}


@end

static void addRoundedRectToPath(CGContextRef context, CGRect rect,
                                float ovalWidth,float ovalHeight)

{
    float fw, fh;

    if (ovalWidth == 0 || ovalHeight == 0) {// 1
        CGContextAddRect(context, rect);
        return;
    }

    CGContextSaveGState(context);// 2

    CGContextTranslateCTM (context, CGRectGetMinX(rect),// 3
                           CGRectGetMinY(rect));
    CGContextScaleCTM (context, ovalWidth, ovalHeight);// 4
    fw = CGRectGetWidth (rect) / ovalWidth;// 5
    fh = CGRectGetHeight (rect) / ovalHeight;// 6

    CGContextMoveToPoint(context, fw, fh/2); // 7
    CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);// 8
    CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);// 9
    CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);// 10
    CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // 11
    CGContextClosePath(context);// 12

    CGContextRestoreGState(context);// 13
}

Thanks for this super helpful post. In case anyone out there (like me!) wants to just have a completely empty cell background in lieu of customizing it through images/text/other content in IB and cannot figure out how the hell to get rid of the dumb border/padding/background even though you set it to clear in IB... here's the code I used that did the trick!


- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {

    static NSString *cellId = @"cellId";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellId];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"EditTableViewCell" owner:self options:nil];
        cell = cellIBOutlet;
        self.cellIBOutlet = nil;
    }

    cell.backgroundView = [[[UIView alloc] initWithFrame: CGRectZero] autorelease];
    [cell.backgroundView setNeedsDisplay];

    ... any other cell customizations ...

    return cell;
}

Hopefully that'll help someone else! Seems to work like a charm.


To change the table view border color:

In.h:

#import <QuartzCore/QuartzCore.h>

In .m:

tableView.layer.masksToBounds=YES;
tableView.layer.borderWidth = 1.0f;
tableView.layer.borderColor = [UIColor whiteColor].CGColor;

Much thanks to all who posted their code. This is very useful.

I derived a similar solution to change the highlight color for grouped table view cells. Basically the UITableViewCell's selectedBackgroundView (not the backgroundView). Which even on iPhone OS 3.0 still needs this PITA solution, as far as I can tell...

The code below has the changes for rendering the highlight with a gradient instead of one solid color. Also the border rendering is removed. Enjoy.

//
//  CSCustomCellBackgroundView.h
//

#import <UIKit/UIKit.h>

typedef enum  
{
    CustomCellBackgroundViewPositionTop, 
    CustomCellBackgroundViewPositionMiddle, 
    CustomCellBackgroundViewPositionBottom,
    CustomCellBackgroundViewPositionSingle,
    CustomCellBackgroundViewPositionPlain
} CustomCellBackgroundViewPosition;

@interface CSCustomCellBackgroundView : UIView 
{
    CustomCellBackgroundViewPosition position;
 CGGradientRef gradient;
}

@property(nonatomic) CustomCellBackgroundViewPosition position;

@end



//
//  CSCustomCellBackgroundView.m
//

#import "CSCustomCellBackgroundView.h"



#define ROUND_SIZE 10


static void addRoundedRectToPath(CGContextRef context, CGRect rect,
         float ovalWidth,float ovalHeight);


@implementation CSCustomCellBackgroundView


@synthesize position;

- (BOOL) isOpaque 
{
    return NO;
}

- (id)initWithFrame:(CGRect)frame 
{
    if (self = [super initWithFrame:frame]) 
 {
        // Initialization code
  const float* topCol = CGColorGetComponents([[UIColor redColor] CGColor]);
  const float* bottomCol = CGColorGetComponents([[UIColor blueColor] CGColor]);

  CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
  /*
  CGFloat colors[] =
  {
   5.0 / 255.0, 140.0 / 255.0, 245.0 / 255.0, 1.00,
   1.0 / 255.0,  93.0 / 255.0, 230.0 / 255.0, 1.00,
  };*/
  CGFloat colors[]=
  {
   topCol[0], topCol[1], topCol[2], topCol[3],
   bottomCol[0], bottomCol[1], bottomCol[2], bottomCol[3]
  };
  gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));
  CGColorSpaceRelease(rgb);
    }
    return self;
}


-(void)drawRect:(CGRect)rect 
{
    // Drawing code

    CGContextRef c = UIGraphicsGetCurrentContext();

    if (position == CustomCellBackgroundViewPositionTop) 
 {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, maxy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, maxy, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, maxy);

        // Close the path
        CGContextClosePath(c);

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;
    } 
 else if (position == CustomCellBackgroundViewPositionBottom) 
 {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddArcToPoint(c, minx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, miny);
        // Close the path
        CGContextClosePath(c);

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;
    } 
 else if (position == CustomCellBackgroundViewPositionMiddle) 
 {
        CGFloat minx = CGRectGetMinX(rect) , maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddLineToPoint(c, maxx, miny);
        CGContextAddLineToPoint(c, maxx, maxy);
        CGContextAddLineToPoint(c, minx, maxy);
  // Close the path
        CGContextClosePath(c);

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;
    }
 else if (position == CustomCellBackgroundViewPositionSingle)
 {
        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , midy = CGRectGetMidY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, midy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE);
        // Close the path
        CGContextClosePath(c);              

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;         
 } 
 else if (position == CustomCellBackgroundViewPositionPlain) {
    CGFloat minx = CGRectGetMinX(rect);
    CGFloat miny = CGRectGetMinY(rect), maxy = CGRectGetMaxY(rect) ;
    CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
    return;
}

}

- (void)dealloc 
{
    CGGradientRelease(gradient);
    [super dealloc];
}


- (void) setPosition:(CustomCellBackgroundViewPosition)inPosition
{
 if(position != inPosition)
 {
  position = inPosition;
  [self setNeedsDisplay];
 }
}

@end


static void addRoundedRectToPath(CGContextRef context, CGRect rect,
         float ovalWidth,float ovalHeight)

{
    float fw, fh;

    if (ovalWidth == 0 || ovalHeight == 0) {// 1
        CGContextAddRect(context, rect);
        return;
    }

    CGContextSaveGState(context);// 2

    CGContextTranslateCTM (context, CGRectGetMinX(rect),// 3
         CGRectGetMinY(rect));
    CGContextScaleCTM (context, ovalWidth, ovalHeight);// 4
    fw = CGRectGetWidth (rect) / ovalWidth;// 5
    fh = CGRectGetHeight (rect) / ovalHeight;// 6

    CGContextMoveToPoint(context, fw, fh/2); // 7
    CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);// 8
    CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);// 9
    CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);// 10
    CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // 11
    CGContextClosePath(context);// 12

    CGContextRestoreGState(context);// 13
}

You can customize the border color by setting

tableView.separatorColor

Thank you for the code, it's just what I was looking for. I have also added the following code to Vimal's code, to implement the case of a CustomCellBackgroundViewPositionSingle cell. (All four corners are rounded.)


else if (position == CustomCellBackgroundViewPositionSingle)
{
        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , midy = CGRectGetMidY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, midy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE);

        // Close the path
        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);                
        return;     
}

I know the answers are relating to changing grouped table cells, but in case someone is wanting to also change the tableview's background color:

Not only do you need to set:

tableview.backgroundColor = color;

You also need to change or get rid of the background view:

tableview.backgroundView = nil;  

This task can be easily done using PrettyKit by adding about 5 lines of code. If you use nib files or storyboard, also do not forget to apply this little hack . When you use this approach, you should subclass your cell from PrettyTableViewCell:

#import <PrettyKit/PrettyKit.h>

@class RRSearchHistoryItem;

@interface RRSearchHistoryCell : PrettyTableViewCell

This is example of my cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  static NSString *cellIdentifier = @"RRSearchHistoryCell";

  RRSearchHistoryCell *cell = (RRSearchHistoryCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];

  if ( cell == nil ) {

    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"RRSearchHistoryCell" owner:self options:nil];
    cell = topLevelObjects[0];
    cell.gradientStartColor = RGB(0xffffff);
    cell.gradientEndColor = RGB(0xf3f3f3);

  }

  RRSearchHistoryItem *item = _historyTableData[indexPath.row];
  [cell setHistoryItem:item];


  [cell prepareForTableView:tableView indexPath:indexPath];

  return cell;
}

I know the answers are relating to changing grouped table cells, but in case someone is wanting to also change the tableview's background color:

Not only do you need to set:

tableview.backgroundColor = color;

You also need to change or get rid of the background view:

tableview.backgroundView = nil;  

Thanks for this super helpful post. In case anyone out there (like me!) wants to just have a completely empty cell background in lieu of customizing it through images/text/other content in IB and cannot figure out how the hell to get rid of the dumb border/padding/background even though you set it to clear in IB... here's the code I used that did the trick!


- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {

    static NSString *cellId = @"cellId";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: cellId];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"EditTableViewCell" owner:self options:nil];
        cell = cellIBOutlet;
        self.cellIBOutlet = nil;
    }

    cell.backgroundView = [[[UIView alloc] initWithFrame: CGRectZero] autorelease];
    [cell.backgroundView setNeedsDisplay];

    ... any other cell customizations ...

    return cell;
}

Hopefully that'll help someone else! Seems to work like a charm.


One thing I ran into with the above CustomCellBackgroundView code from Mike Akers which might be useful to others:

cell.backgroundView doesn't get automatically redrawn when cells are reused, and changes to the backgroundView's position var don't affect reused cells. That means long tables will have incorrectly drawn cell.backgroundViews given their positions.

To fix this without having to create a new backgroundView every time a row is displayed, call [cell.backgroundView setNeedsDisplay] at the end of your -[UITableViewController tableView:cellForRowAtIndexPath:]. Or for a more reusable solution, override CustomCellBackgroundView's position setter to include a [self setNeedsDisplay].


To change the table view border color:

In.h:

#import <QuartzCore/QuartzCore.h>

In .m:

tableView.layer.masksToBounds=YES;
tableView.layer.borderWidth = 1.0f;
tableView.layer.borderColor = [UIColor whiteColor].CGColor;

First of all thanks for this code. I have made some drawing changes in this function to remove corner problem of drawing.

-(void)drawRect:(CGRect)rect 
{
    // Drawing code

    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(c, [fillColor CGColor]);
    CGContextSetStrokeColorWithColor(c, [borderColor CGColor]);
    CGContextSetLineWidth(c, 2);

    if (position == CustomCellBackgroundViewPositionTop) {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, maxy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, maxy, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, maxy);

        // Close the path
        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);        
        return;
    } else if (position == CustomCellBackgroundViewPositionBottom) {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny ;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddArcToPoint(c, minx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, miny);
        // Close the path
        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);    
        return;
    } else if (position == CustomCellBackgroundViewPositionMiddle) {
        CGFloat minx = CGRectGetMinX(rect) , maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny ;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddLineToPoint(c, maxx, miny);
        CGContextAddLineToPoint(c, maxx, maxy);
        CGContextAddLineToPoint(c, minx, maxy);

        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);    
        return;
    }
}

You can customize the border color by setting

tableView.separatorColor

Thank you for the code, it's just what I was looking for. I have also added the following code to Vimal's code, to implement the case of a CustomCellBackgroundViewPositionSingle cell. (All four corners are rounded.)


else if (position == CustomCellBackgroundViewPositionSingle)
{
        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , midy = CGRectGetMidY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, midy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE);

        // Close the path
        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);                
        return;     
}

I have been having problems with this and tried lots of combinations of things as I noticed that for some cells it worked fine but not for others.

Strangely I found out that it is possible to set the cell.backgroundColor to lightGrayColor and all works perfectly - but blueColor caused me problems of not updating the outside edges.

Unless it is really important to use green - perhaps you might want to try this. It might be that this is a feature to get people to use only grey colours when indicating a cell is selected.


This task can be easily done using PrettyKit by adding about 5 lines of code. If you use nib files or storyboard, also do not forget to apply this little hack . When you use this approach, you should subclass your cell from PrettyTableViewCell:

#import <PrettyKit/PrettyKit.h>

@class RRSearchHistoryItem;

@interface RRSearchHistoryCell : PrettyTableViewCell

This is example of my cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  static NSString *cellIdentifier = @"RRSearchHistoryCell";

  RRSearchHistoryCell *cell = (RRSearchHistoryCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];

  if ( cell == nil ) {

    NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"RRSearchHistoryCell" owner:self options:nil];
    cell = topLevelObjects[0];
    cell.gradientStartColor = RGB(0xffffff);
    cell.gradientEndColor = RGB(0xf3f3f3);

  }

  RRSearchHistoryItem *item = _historyTableData[indexPath.row];
  [cell setHistoryItem:item];


  [cell prepareForTableView:tableView indexPath:indexPath];

  return cell;
}

Much thanks to all who posted their code. This is very useful.

I derived a similar solution to change the highlight color for grouped table view cells. Basically the UITableViewCell's selectedBackgroundView (not the backgroundView). Which even on iPhone OS 3.0 still needs this PITA solution, as far as I can tell...

The code below has the changes for rendering the highlight with a gradient instead of one solid color. Also the border rendering is removed. Enjoy.

//
//  CSCustomCellBackgroundView.h
//

#import <UIKit/UIKit.h>

typedef enum  
{
    CustomCellBackgroundViewPositionTop, 
    CustomCellBackgroundViewPositionMiddle, 
    CustomCellBackgroundViewPositionBottom,
    CustomCellBackgroundViewPositionSingle,
    CustomCellBackgroundViewPositionPlain
} CustomCellBackgroundViewPosition;

@interface CSCustomCellBackgroundView : UIView 
{
    CustomCellBackgroundViewPosition position;
 CGGradientRef gradient;
}

@property(nonatomic) CustomCellBackgroundViewPosition position;

@end



//
//  CSCustomCellBackgroundView.m
//

#import "CSCustomCellBackgroundView.h"



#define ROUND_SIZE 10


static void addRoundedRectToPath(CGContextRef context, CGRect rect,
         float ovalWidth,float ovalHeight);


@implementation CSCustomCellBackgroundView


@synthesize position;

- (BOOL) isOpaque 
{
    return NO;
}

- (id)initWithFrame:(CGRect)frame 
{
    if (self = [super initWithFrame:frame]) 
 {
        // Initialization code
  const float* topCol = CGColorGetComponents([[UIColor redColor] CGColor]);
  const float* bottomCol = CGColorGetComponents([[UIColor blueColor] CGColor]);

  CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
  /*
  CGFloat colors[] =
  {
   5.0 / 255.0, 140.0 / 255.0, 245.0 / 255.0, 1.00,
   1.0 / 255.0,  93.0 / 255.0, 230.0 / 255.0, 1.00,
  };*/
  CGFloat colors[]=
  {
   topCol[0], topCol[1], topCol[2], topCol[3],
   bottomCol[0], bottomCol[1], bottomCol[2], bottomCol[3]
  };
  gradient = CGGradientCreateWithColorComponents(rgb, colors, NULL, sizeof(colors)/(sizeof(colors[0])*4));
  CGColorSpaceRelease(rgb);
    }
    return self;
}


-(void)drawRect:(CGRect)rect 
{
    // Drawing code

    CGContextRef c = UIGraphicsGetCurrentContext();

    if (position == CustomCellBackgroundViewPositionTop) 
 {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, maxy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, maxy, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, maxy);

        // Close the path
        CGContextClosePath(c);

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;
    } 
 else if (position == CustomCellBackgroundViewPositionBottom) 
 {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddArcToPoint(c, minx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, miny);
        // Close the path
        CGContextClosePath(c);

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;
    } 
 else if (position == CustomCellBackgroundViewPositionMiddle) 
 {
        CGFloat minx = CGRectGetMinX(rect) , maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddLineToPoint(c, maxx, miny);
        CGContextAddLineToPoint(c, maxx, maxy);
        CGContextAddLineToPoint(c, minx, maxy);
  // Close the path
        CGContextClosePath(c);

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;
    }
 else if (position == CustomCellBackgroundViewPositionSingle)
 {
        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , midy = CGRectGetMidY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, midy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, midy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, minx, maxy, minx, midy, ROUND_SIZE);
        // Close the path
        CGContextClosePath(c);              

  CGContextSaveGState(c);
  CGContextClip(c);
  CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
  CGContextRestoreGState(c);

        return;         
 } 
 else if (position == CustomCellBackgroundViewPositionPlain) {
    CGFloat minx = CGRectGetMinX(rect);
    CGFloat miny = CGRectGetMinY(rect), maxy = CGRectGetMaxY(rect) ;
    CGContextDrawLinearGradient(c, gradient, CGPointMake(minx,miny), CGPointMake(minx,maxy), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
    return;
}

}

- (void)dealloc 
{
    CGGradientRelease(gradient);
    [super dealloc];
}


- (void) setPosition:(CustomCellBackgroundViewPosition)inPosition
{
 if(position != inPosition)
 {
  position = inPosition;
  [self setNeedsDisplay];
 }
}

@end


static void addRoundedRectToPath(CGContextRef context, CGRect rect,
         float ovalWidth,float ovalHeight)

{
    float fw, fh;

    if (ovalWidth == 0 || ovalHeight == 0) {// 1
        CGContextAddRect(context, rect);
        return;
    }

    CGContextSaveGState(context);// 2

    CGContextTranslateCTM (context, CGRectGetMinX(rect),// 3
         CGRectGetMinY(rect));
    CGContextScaleCTM (context, ovalWidth, ovalHeight);// 4
    fw = CGRectGetWidth (rect) / ovalWidth;// 5
    fh = CGRectGetHeight (rect) / ovalHeight;// 6

    CGContextMoveToPoint(context, fw, fh/2); // 7
    CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);// 8
    CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);// 9
    CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);// 10
    CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // 11
    CGContextClosePath(context);// 12

    CGContextRestoreGState(context);// 13
}

I have been having problems with this and tried lots of combinations of things as I noticed that for some cells it worked fine but not for others.

Strangely I found out that it is possible to set the cell.backgroundColor to lightGrayColor and all works perfectly - but blueColor caused me problems of not updating the outside edges.

Unless it is really important to use green - perhaps you might want to try this. It might be that this is a feature to get people to use only grey colours when indicating a cell is selected.


One thing I ran into with the above CustomCellBackgroundView code from Mike Akers which might be useful to others:

cell.backgroundView doesn't get automatically redrawn when cells are reused, and changes to the backgroundView's position var don't affect reused cells. That means long tables will have incorrectly drawn cell.backgroundViews given their positions.

To fix this without having to create a new backgroundView every time a row is displayed, call [cell.backgroundView setNeedsDisplay] at the end of your -[UITableViewController tableView:cellForRowAtIndexPath:]. Or for a more reusable solution, override CustomCellBackgroundView's position setter to include a [self setNeedsDisplay].


First of all thanks for this code. I have made some drawing changes in this function to remove corner problem of drawing.

-(void)drawRect:(CGRect)rect 
{
    // Drawing code

    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(c, [fillColor CGColor]);
    CGContextSetStrokeColorWithColor(c, [borderColor CGColor]);
    CGContextSetLineWidth(c, 2);

    if (position == CustomCellBackgroundViewPositionTop) {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny + 1;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, maxy);
        CGContextAddArcToPoint(c, minx, miny, midx, miny, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, miny, maxx, maxy, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, maxy);

        // Close the path
        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);        
        return;
    } else if (position == CustomCellBackgroundViewPositionBottom) {

        CGFloat minx = CGRectGetMinX(rect) , midx = CGRectGetMidX(rect), maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny ;

        maxx = maxx - 1;
        maxy = maxy - 1;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddArcToPoint(c, minx, maxy, midx, maxy, ROUND_SIZE);
        CGContextAddArcToPoint(c, maxx, maxy, maxx, miny, ROUND_SIZE);
        CGContextAddLineToPoint(c, maxx, miny);
        // Close the path
        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);    
        return;
    } else if (position == CustomCellBackgroundViewPositionMiddle) {
        CGFloat minx = CGRectGetMinX(rect) , maxx = CGRectGetMaxX(rect) ;
        CGFloat miny = CGRectGetMinY(rect) , maxy = CGRectGetMaxY(rect) ;
        minx = minx + 1;
        miny = miny ;

        maxx = maxx - 1;
        maxy = maxy ;

        CGContextMoveToPoint(c, minx, miny);
        CGContextAddLineToPoint(c, maxx, miny);
        CGContextAddLineToPoint(c, maxx, maxy);
        CGContextAddLineToPoint(c, minx, maxy);

        CGContextClosePath(c);
        // Fill & stroke the path
        CGContextDrawPath(c, kCGPathFillStroke);    
        return;
    }
}

Examples related to ios

Adding a UISegmentedControl to UITableView Crop image to specified size and picture location Undefined Symbols error when integrating Apptentive iOS SDK via Cocoapods Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Autoresize View When SubViews are Added Warp \ bend effect on a UIView? Speech input for visually impaired users without the need to tap the screen make UITableViewCell selectable only while editing Xcode 12, building for iOS Simulator, but linking in object file built for iOS, for architecture arm64

Examples related to objective-c

Adding a UISegmentedControl to UITableView Keep placeholder text in UITextField on input in IOS Accessing AppDelegate from framework? Warp \ bend effect on a UIView? Use NSInteger as array index Detect if the device is iPhone X Linker Command failed with exit code 1 (use -v to see invocation), Xcode 8, Swift 3 ITSAppUsesNonExemptEncryption export compliance while internal testing? How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem? Change status bar text color to light in iOS 9 with Objective-C

Examples related to iphone

Detect if the device is iPhone X Xcode 8 shows error that provisioning profile doesn't include signing certificate Access files in /var/mobile/Containers/Data/Application without jailbreaking iPhone Certificate has either expired or has been revoked Missing Compliance in Status when I add built for internal testing in Test Flight.How to solve? cordova run with ios error .. Error code 65 for command: xcodebuild with args: "Could not find Developer Disk Image" Reason: no suitable image found iPad Multitasking support requires these orientations How to insert new cell into UITableView in Swift

Examples related to uitableview

Adding a UISegmentedControl to UITableView make UITableViewCell selectable only while editing How to fix Error: this class is not key value coding-compliant for the key tableView.' UITableView example for Swift Swift - how to make custom header for UITableView? How to insert new cell into UITableView in Swift How to detect tableView cell touched or clicked in swift Dynamic Height Issue for UITableView Cells (Swift) UIButton action in table view cell How to get the indexpath.row when an element is activated?

Examples related to cocoa-touch

Include of non-modular header inside framework module Move textfield when keyboard appears swift Create space at the beginning of a UITextField Navigation bar with UIImage for title Generate a UUID on iOS from Swift How do I write a custom init for a UIView subclass in Swift? creating custom tableview cells in swift How would I create a UIAlertView in Swift? Get current NSDate in timestamp format How do you add an in-app purchase to an iOS application?