Thursday, November 13, 2008

Creating Transparent UIViews - a Rounded Rect View

I recently needed a view that had the shape of a rounded rectangle. Drawing a rounded rect using Core Graphics isn't particularly hard. As a matter of fact, the QuartzDemo sample code shows one way to do it. So, I dutifully created the view to draw a rounded rect. No matter what I did, however, it drew white all the way to the boundary of the view. My first version of the rounded rectangle view came out decidedly non-rounded.

The problem was that I was using the backgroundColor property inherited from UIView to identify what color to draw the rounded rectangle. But, and here's the point to remember today: if you need objects below yours to show through parts of your view without using alpha, the backgroundColor property MUST be set to [UIColor clearColor]. This is true even if you do not call drawRect on super.

The other thing you need to do is make sure that the opaqueproperty is set to NO. Ordinarily, opaque should be set to YES as an optimization, but that optimization isn't available to us if we want stuff to show through from below.

My second attempt at a rounded rectangle view was decidedly more rounded:

Because I knew that I would likely have need of a rounded rectangle view in the future, I wrote it generically. And, here it is, may it serve you well:

RoundedRectView.h
//
// RoundedRectView.h
//
// Created by Jeff LaMarche on 11/13/08.

#import <UIKit/UIKit.h>

#define kDefaultStrokeColor [UIColor whiteColor]
#define kDefaultRectColor [UIColor whiteColor]
#define kDefaultStrokeWidth 1.0
#define kDefaultCornerRadius 30.0

@interface RoundedRectView : UIView {
UIColor *strokeColor;
UIColor *rectColor;
CGFloat strokeWidth;
CGFloat cornerRadius;
}
@property (nonatomic, retain) UIColor *strokeColor;
@property (nonatomic, retain) UIColor *rectColor;
@property CGFloat strokeWidth;
@property CGFloat cornerRadius;
@end



RoundedRectView.m
//
// RoundedRectView.m
//
// Created by Jeff LaMarche on 11/13/08.

#import "RoundedRectView.h"

@implementation RoundedRectView
@synthesize strokeColor;
@synthesize rectColor;
@synthesize strokeWidth;
@synthesize cornerRadius;
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super initWithCoder:decoder])
{
self.strokeColor = kDefaultStrokeColor;
self.backgroundColor = [UIColor clearColor];
self.strokeWidth = kDefaultStrokeWidth;
self.rectColor = kDefaultRectColor;
self.cornerRadius = kDefaultCornerRadius;
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
// Initialization code
self.opaque = NO;
self.strokeColor = kDefaultStrokeColor;
self.backgroundColor = [UIColor clearColor];
self.rectColor = kDefaultRectColor;
self.strokeWidth = kDefaultStrokeWidth;
self.cornerRadius = kDefaultCornerRadius;
}
return self;
}
- (void)setBackgroundColor:(UIColor *)newBGColor
{
// Ignore any attempt to set background color - backgroundColor must stay set to clearColor
// We could throw an exception here, but that would cause problems with IB, since backgroundColor
// is a palletized property, IB will attempt to set backgroundColor for any view that is loaded
// from a nib, so instead, we just quietly ignore this.
//
// Alternatively, we could put an NSLog statement here to tell the programmer to set rectColor...
}
- (void)setOpaque:(BOOL)newIsOpaque
{
// Ignore attempt to set opaque to YES.
}
- (void)drawRect:(CGRect)rect {

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, strokeWidth);
CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
CGContextSetFillColorWithColor(context, self.rectColor.CGColor);

CGRect rrect = self.bounds;

CGFloat radius = cornerRadius;
CGFloat width = CGRectGetWidth(rrect);
CGFloat height = CGRectGetHeight(rrect);

// Make sure corner radius isn't larger than half the shorter side
if (radius > width/2.0)
radius = width/2.0;
if (radius > height/2.0)
radius = height/2.0;

CGFloat minx = CGRectGetMinX(rrect);
CGFloat midx = CGRectGetMidX(rrect);
CGFloat maxx = CGRectGetMaxX(rrect);
CGFloat miny = CGRectGetMinY(rrect);
CGFloat midy = CGRectGetMidY(rrect);
CGFloat maxy = CGRectGetMaxY(rrect);
CGContextMoveToPoint(context, minx, midy);
CGContextAddArcToPoint(context, minx, miny, midx, miny, radius);
CGContextAddArcToPoint(context, maxx, miny, maxx, midy, radius);
CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, radius);
CGContextAddArcToPoint(context, minx, maxy, minx, midy, radius);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}

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

@end

No comments:

Post a Comment