Friday, May 22, 2009

Rounding Corners on a UIView

I've been working on an iPhone application for the last couple of months in my spare time. One need I came across that I couldn't find an existing solution for was being able to round the corners on an existing view. Specifically, I need to include a UITableView as a child view of another view, but UITableView has hard corners, which looks wrong on the iPhone. This class will soften the corners.

The RoundedView should be added last to the container that holds it, so it is drawn on top. Events are passed to the next responder. You can set the radius of the corners and the color of the fill. You can also turn on and off which corners are drawn rounded.

Sample Usage

- (void)viewDidLoad {
[super viewDidLoad];

UIView *tableView = [self.view viewWithTag:kTableViewTag];
RoundedView *rv = [[RoundedView alloc] initWithFrame:tableView.frame];
rv.cornerColor = [UIColor colorBlue];
rv.radius = 10;
rv.roundLowerLeft = rv.roundLowerRight = NO;
[self.view addSubview:rv];
}

RoundedView.h

#import <UIKit/UIKit.h>

@interface RoundedView : UIView {
int radius;
UIColor *cornerColor;
BOOL roundUpperLeft, roundUpperRight,
roundLowerLeft, roundLowerRight;
}

@property (nonatomic,retain) UIColor *cornerColor;
@property (nonatomic) int radius;
@property (nonatomic) BOOL roundUpperLeft;
@property (nonatomic) BOOL roundUpperRight;
@property (nonatomic) BOOL roundLowerLeft;
@property (nonatomic) BOOL roundLowerRight;

@end

RoundedView.m

#import "RoundedView.h"

// Private methods for RoundedView
@interface RoundedView()
-(void) drawRoundedCornersInRect:(CGRect) rect inContext:(CGContextRef) c;
-(void) drawCornerInContext:(CGContextRef)c cornerX:(int) x
cornerY:(int) y arcEndX:(int) endX arcEndY:(int) endY;
@end

@implementation RoundedView

@synthesize radius, cornerColor, roundLowerLeft, roundLowerRight;
@synthesize roundUpperLeft, roundUpperRight;

-(id) initWithFrame:(CGRect) frame {
if (self=[super initWithFrame:frame]) {
self.cornerColor=[UIColor clearColor];
self.backgroundColor=[UIColor clearColor];
self.opaque=NO;
radius=5;
roundUpperLeft = roundUpperRight = YES;
roundLowerLeft = roundLowerRight = YES;
}
return self;
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// We pretend like no points are inside our bounds so the events
// can continue up the responder chain
return NO;
}

-(void) drawRect:(CGRect) rect {
CGContextRef c = UIGraphicsGetCurrentContext();
if (c != nil) {
CGContextSetFillColorWithColor(c, self.cornerColor.CGColor);
[self drawRoundedCornersInRect:self.bounds inContext:c];
CGContextFillPath(c);
}
}

-(void) drawCornerInContext:(CGContextRef)c cornerX:(int) x cornerY:(int) y
arcEndX:(int) endX arcEndY:(int) endY {
CGContextMoveToPoint(c, x, endY);
CGContextAddArcToPoint(c, x, y, endX, y, radius);
CGContextAddLineToPoint(c, x, y);
CGContextAddLineToPoint(c, x, endY);
}

-(void) drawRoundedCornersInRect:(CGRect) rect inContext:(CGContextRef) c {
int x_left = rect.origin.x;
int x_left_center = rect.origin.x + radius;
int x_right_center = rect.origin.x + rect.size.width - radius;
int x_right = rect.origin.x + rect.size.width;
int y_top = rect.origin.y;
int y_top_center = rect.origin.y + radius;
int y_bottom_center = rect.origin.y + rect.size.height - radius;
int y_bottom = rect.origin.y + rect.size.height;

if (roundUpperLeft) {
[self drawCornerInContext:c cornerX: x_left cornerY: y_top
arcEndX: x_left_center arcEndY: y_top_center];
}

if (roundUpperRight) {
[self drawCornerInContext:c cornerX: x_right cornerY: y_top
arcEndX: x_right_center arcEndY: y_top_center];
}

if (roundLowerRight) {
[self drawCornerInContext:c cornerX: x_right cornerY: y_bottom
arcEndX: x_right_center arcEndY: y_bottom_center];
}

if (roundLowerLeft) {
[self drawCornerInContext:c cornerX: x_left cornerY: y_bottom
arcEndX: x_left_center arcEndY: y_bottom_center];
}
}
@end

This code is based off of some code I found that draws a rounded rectangle.

Enjoy and let me know if you make an improvements.
blog comments powered by Disqus