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];
    }
}

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

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.

6 comments:

Jerry said...

I tried using this code but I can't figure out how to set the background color. I want a rounded rectangle where the tips that are rounded off are transparent and the main background is colored. I set background color and I get a normal rectangle.

Travis Jensen said...

It sounds like you want the original code that I based this off of. The link is in the original post.

tim said...

Awesome code - worked right out of the box. Use should do more posts!

tim said...

any chance you could show how to put a border on the rounded view? that would be sweet...

ltgbau said...

Can you show me how to create a real rounded view likes using CRegion in MFC (Windows), is there any way to do that?
Thanks in advance!

Julie said...

Great code. I am trying to figure out how to do this

- UITableView
-----UITableViewCell
-----UITableViewCell
-----UITableViewCell
-----UITableViewCell
------------- UITableView
----------------UITableViewCell
----------------UITableViewCell
----------------UITableViewCell
----------------UITableViewCell
-----UITableViewCell
-----UITableViewCell

Do you know what is the best way?