Monthly Archive:: April 2011

In order to implement your own style ideas or a given layout it might be necessary to create custom UITableViewCells with round corners. In the App the UITableView might look like this:

UITableViewCells with round corners

With your custom UITableViewCell class and some core graphics it’s quite easy to solve this problem.

Open your project in XCode. At first we have to add a new file of type UITableViewCell. I call it “RoundedTableViewCell”.

We have to import the QuartzCore framework which includes all core graphics.

#import <QuartzCore/QuartzCore/>

Don’t forget to add it also to the “frameworks” folder of your project:

add QuartzCore FRamework to your project add QuartzCore framework

In the RoundedTableViewCell.h-file we define a CALayer for every corner of the cell. These small square layers will later on “cover” the round corners where desired.

CALayer* topleft;
CALayer* topright;
CALayer* bottomleft;
CALayer* bottomright;
CALayer* bglayer;
...
@property (nonatomic, retain) CALayer* topleft;
@property (nonatomic, retain) CALayer* topright;
@property (nonatomic, retain) CALayer* bottomleft;
@property (nonatomic, retain) CALayer* bottomright;
@property (nonatomic, retain) CALayer* bglayer;

We also need two BOOLs:

BOOL roundTop;
BOOL roundBottom;
...
@property (nonatomic) BOOL roundTop;
@property (nonatomic) BOOL roundBottom;

.. and two methods to tell the single cell to have round corners at the top and/or the bottom:

-(void) drawRoundTop;
-(void) drawRoundBottom;

That’s it for the .h-file. In the RoundedTableViewCell.m we have now to implement the rounded corners. At first we define a corner radius and a background color for the cell (between #import and @implemetation):

#define rad 15  // radius
#define normalColor [UIColor colorWithRed:0.39 green:0.15 blue:0.24 alpha:1.0]  // cell background color, dark red

Whithin the implementation we have to synthesize our defined CALayers and BOOLs:

@synthesize topleft;
@synthesize topright;
@synthesize bottomleft;
@synthesize bottomright;
@synthesize bglayer;
@synthesize roundTop;
@synthesize roundBottom;

In the initWithStyle-Method we define an UILabel with some style attributes:

// add label
 label = [[UILabel alloc] initWithFrame:self.contentView.frame];
 label.textAlignment = UITextAlignmentLeft;
 label.backgroundColor = [UIColor clearColor];
 label.textColor = [UIColor whiteColor];
 label.font = [UIFont fontWithName:@"Zapfino" size:16];
 [self.contentView addSubview:label];

// initial values
 roundTop = NO;
 roundBottom = NO;
// set layer background color (= cell background color)
 self.layer.backgroundColor = normalColor.CGColor;

Still within the initWithStyle-method, we set the initial cell style without round top or bottom corners and set the cell’s background color:

roundTop = NO;
roundBottom = NO;
self.layer.backgroundColor = normalColor.CGColor;

Our custom drawRect method will draw our round corners, depending on the defined corner radius. Then the round corners are “covered” by drawing a CALayer for every corner:

- (void) drawRect:(CGRect)rect{
	CGRect fr = rect;

	fr.size.width = fr.size.width-2*rad;
	fr.size.height = fr.size.height-1;
	fr.origin.x = rad;

	// draw round corners layer
	bglayer = [CALayer layer];
    bglayer.backgroundColor = normalColor.CGColor;
	bglayer.cornerRadius = rad;
	bglayer.frame = fr;
	bglayer.zPosition = -5;	// important, otherwise delete button does not fire / is covered
	[self.layer addSublayer:bglayer];

	// set label size (adjust to heightForRowAtIndexPath..)
	label.frame = CGRectMake(rad, 5, fr.size.width, fr.size.height);

	// corner layer top left
	topleft = [CALayer layer];
    topleft.backgroundColor = normalColor.CGColor;
	CGRect tl = CGRectMake(rad, 0, rad, rad);
	topleft.frame = tl;
	topleft.zPosition = -4;
	if(roundTop){
		topleft.hidden = YES;
	}else {
		topleft.hidden = NO;
	}
    [self.layer addSublayer:topleft];

	// corner layer top right
	topright = [CALayer layer];
    topright.backgroundColor = normalColor.CGColor;
	topright.frame = CGRectMake(fr.size.width, 0, rad, rad);
	topright.zPosition = -3;
	if(roundTop){
		topright.hidden = YES;
	}
	else {
		topright.hidden = NO;
	}
    [self.layer addSublayer:topright];

	// corner layer bottom left
	bottomleft = [CALayer layer];
    bottomleft.backgroundColor = normalColor.CGColor;
	bottomleft.frame = CGRectMake(rad, fr.size.height-rad, rad, rad);
	bottomleft.zPosition = -2;
	if(roundBottom){
		bottomleft.hidden = YES;
	}else {
		bottomleft.hidden = NO;
	}
    [self.layer addSublayer:bottomleft];

	// corner layer bottom right
	bottomright = [CALayer layer];
	bottomright.backgroundColor = normalColor.CGColor;
	bottomright.frame = CGRectMake(fr.size.width, fr.size.height-rad, rad, rad);
	bottomright.zPosition = -1;
	if(roundBottom){
		bottomright.hidden = YES;
	}else {
		bottomright.hidden = NO;
	}
    [self.layer addSublayer:bottomright];

	[super drawRect:rect];

}

Now we implement both methods to show round corners on top or bottom of the cell by hiding the appropriate CALayers:

-(void) drawRoundTop{
roundTop = YES;
topleft.hidden = YES;
topright.hidden = YES;
}

For the cell bottom:

-(void) drawRoundBottom{
roundBottom = YES;
bottomleft.hidden = YES;
bottomright.hidden = YES;
}

Ok, our custom UITableViewCell class is ready for use. Therefore we open our UITableViewController file (.m). In the viewDidLoad method we have to implement at least the first line of the following code (if not, the standard table background view will hide our new cells).

// important! without this line it does not work!
[self.tableView setBackgroundView:[[[UIView alloc] init] autorelease]];
// set plain background color
 [self.tableView setBackgroundColor:[UIColor colorWithRed:0.69 green:0.81 blue:0.79 alpha:1.0]];
// remove seperator color
 self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
 [self.tableView setSeparatorColor:[UIColor clearColor]];

In my case I have defined two sections, one with normal cells and one with rounded cells, like a separate block. The cell type is defined in the cellForRowAtIndexPath method like this:

if (indexPath.section == SECTION_NORMAL) {

 static NSString *CellIdentifier = @"Cell";

 NormalTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
 if (cell == nil) {
 cell = [[[NormalTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
 }
 cell.label.text = @"normal cell";
 return cell;

 } else if (indexPath.section == SECTION_ROUNDED) {
// define round cell
 static NSString *CellRoundedIdentifier = @"RoundedTableViewCell";
 RoundedTableViewCell *cell = (RoundedTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellRoundedIdentifier];
 if (cell == nil) {
 cell = [[[RoundedTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellRoundedIdentifier] autorelease];
 }

 // Configure the cell.
 [cell.label setText:[NSString stringWithFormat:@"my round cell No.%d",(indexPath.row+1)]];
 cell.selectionStyle = UITableViewCellSelectionStyleNone;
 // draw round top corners in first row
 if(indexPath.row == 0){
 [cell drawRoundTop];
 }
// draw round corners in last row
if (indexPath.row == [self.tableView  numberOfRowsInSection:indexPath.section]-1) {
 [cell drawRoundBottom];
 }

 return cell;
 }

In order to set a margin between both sections, use the heightForHeaderInSection and the viewForHeaderInSection methods:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

 UIView* header = nil;
 if(section == SECTION_ROUNDED){
 CGRect rect = CGRectMake(0, 0, self.tableView.frame.size.width, [self tableView:(tableView) heightForHeaderInSection:section]);
 header = [[UIView alloc] initWithFrame:rect];

 }
 return header;
}

- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
 CGFloat h = 0;
 if(section == SECTION_ROUNDED){
 h = 15;
 }
 return h;
}

You can download the zipped XCode example project from github.

When developing Apps for the Android OS you might end up in the situation where you have common Activities or Services which could be reused in multiple Apps with just a little modification. As you don’t want to copy these classes into every single project – which would lead to a hardly maintainable code – you would seek a way to reference the source of these classes from multiple projects. For non-android applications an approach for this problem would be to break up the code into multiple libraries so that the required functionality can be referenced from multiple projects. As long as you are not interacting with external resources this attempt is also possible in Android projects (build a jar and reference it from the projects). But if you are using external resources these classes can’t be used within a library. This is caused by the fact that the Android SDK won’t generate matching ids within the “R” class for external resources which are included in a referenced jar file. In this post I want to show you different solutions for this problem which can avoid the necessity to copy the source code into the projects.