Making a count indicator

Count indicator exampleJust recently I wrote a list app for iPhone, so I could maintain lists with items in them.  I wanted to jazz it up a bit by using a count indicator similar to the one you see in the mail app, which shows you how many messages you’ve had from someone (you know the one… the little bubble with a number in it).  Anyway, turns out that Apple don’t provide a mechanism for this so I wrote my own one.  You can see it over there to the left (a little gray bubble with a count in it) and this post basically shows you how I achieved it (with code and an example project).

All it’s essentially doing is using Core Graphics to draw a line. That’s all. One line, except to get the rounded ends I’m using a cap as demonstrated in many of the Apple examples. I thought this was easier than figuring out how to round it off manually. Then I take the text and paste it as close to center as I can.

Now it’s not perfect I know… changing the font size doesn’t always vertically align the number properly – but it does look nice enough, and I’m sure someone can make use of this even if a little tweaking is required.  At around 14 point font size it looks good….

It’s simply a subclass of UIView which draws itself into a frame, based on the way you initialise it.  In my own app, I use the counter in a custom table cell to show how many child objects I have in Core Data.  For those of you who are interested, here’s the class code…

CountIndicator.h

#import 

@interface CountIndicator : UIView {
	CGFloat fontSize;
	CGFloat width;
	NSMutableString *indicatorText;
}

@property (nonatomic) CGFloat fontSize;
@property (nonatomic) CGFloat width;
@property (nonatomic, retain) NSMutableString *indicatorText;

- (id)initWithPoint:(CGPoint)xy andFontSize:(CGFloat)fs andWidth:(CGFloat)wth;

@end

CountIndicator.m

#import "CountIndicator.h"

#define    kIndColor_R     0.5f
#define    kIndColor_G     0.5f
#define    kIndColor_B     0.5f
#define    kIndColor_A     1.0f

@implementation CountIndicator

@synthesize fontSize, width, indicatorText;

// Initialise
- (id)initWithPoint:(CGPoint)xy andFontSize:(CGFloat)fs andWidth:(CGFloat)wth
{
	// Set up the frame
	CGRect frame;
	frame.origin.x = xy.x;
	frame.origin.y = xy.y;
	frame.size.width = wth;
	frame.size.height = fs;
	
	// Get initialised with given frame size and set defaults
	if (self = [super initWithFrame:frame]) {
		self.fontSize = fs;
		self.indicatorText = [NSMutableString stringWithFormat:@""];
	}
	
	return self;
}

// Custom drawing
- (void)drawRect:(CGRect)rect
{
	// Get graphics context
	CGContextRef context = UIGraphicsGetCurrentContext();

	// Calculate line position
	CGFloat startPosX = rect.size.height / 2;
	CGFloat startPosY = rect.size.height / 2;
	CGFloat endPosX = rect.size.width - startPosX;
	CGFloat endPosY = startPosY;

	// Get the size of the text area based on the string (we need this to centre the text)
	CGSize textSize = [self.indicatorText sizeWithFont:[UIFont fontWithName:@"Helvetica" size:self.fontSize]];
	
	// Set the stroke colour
	CGContextSetRGBStrokeColor(context, kIndColor_R, kIndColor_G, kIndColor_B, kIndColor_A);
	
	// Draw the line and cap the ends
	CGContextMoveToPoint(context, startPosX, startPosY);
	CGContextAddLineToPoint(context, endPosX, endPosY);	
	CGContextSetLineWidth(context, rect.size.height);	
	CGContextSetLineCap(context, 1);
	CGContextStrokePath(context);
	
	// Show text
	CGAffineTransform transform = CGAffineTransformMake( 1.0,0.0, 0.0, -1.0, 0.0, 0.0  );
	CGContextSetTextMatrix(context, transform);
	CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
	CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
	CGContextSelectFont(context, "Helvetica", self.fontSize, kCGEncodingMacRoman);
	CGContextSetTextDrawingMode(context, kCGTextFill);
	
	// Center the text in the indicator bubble
	CGContextSetTextPosition(context, (rect.size.width/2)-(textSize.width/2), (rect.size.height) - 2.0f );
	CGContextSetShouldSmoothFonts(context, false);
	CGContextShowText(context, [self.indicatorText UTF8String], strlen([self.indicatorText UTF8String]));
}

// Be good memory citizen!
- (void)dealloc
{
    [indicatorText release];
    [super dealloc];
}

@end

And just because it’s friday, here’s an example of how you can use it. Try creating a blank view controller and putting this code in there to check out what it looks like….

- (void)viewDidLoad
{
	// Test out the indicator
	CountIndicator *testCI = [[CountIndicator alloc] initWithPoint:CGPointMake(5.0f, 5.0f) andFontSize:13.0f andWidth:26.0f];
	testCI.backgroundColor = [UIColor clearColor];
	testCI.indicatorText = [NSString stringWithFormat:@"42"];
	[self.view addSubview:testCI];
	[testCI release];
}

Remember to include the header file! Anyway, hope that helped someone. Comments, suggestions and harsh reality checks all welcome. Have a good weekend. Oh, and if you get really stuck … here’s the example project file

Leave a Reply