Skip to content

Instantly share code, notes, and snippets.

@wujun
Created February 21, 2012 06:35
Show Gist options
  • Save wujun/1874350 to your computer and use it in GitHub Desktop.
Save wujun/1874350 to your computer and use it in GitHub Desktop.
custom badge from Michael F. Kamprath
//
// MKNumberBadgeView.h
//
// Copyright 2009 Michael F. Kamprath
// michael@claireware.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
// MKNumberBadgeView
// -----------------
//
// Use this class to display a badge containing an integer value.Similar to the app icon badges that the iPhone OS permits.
//
// Notes:
// * When creating the view, the frame used should be larger than the expected visual size of the badge view. Use the alignment
// property to control the horizontal placement of the badge within the view's bounds. The badge will always be vertically
// centered for the badge itself ignoring the size fo the shadow if it is enabled.
// * The view's background color is automatically set to clear. If you change the background color, you may get curious results.
//
#import <UIKit/UIKit.h>
@interface MKNumberBadgeView : UIView
{
NSUInteger _value;
UIFont* _font;
UIColor* _fillColor;
UIColor* _strokeColor;
UIColor* _textColor;
NSUInteger _pad;
BOOL _shadow;
BOOL _shine;
UITextAlignment _alignment;
}
// The current value displayed in the badge. Updating the value will update the view's display
@property (assign,nonatomic) NSUInteger value;
// Indicates whether the badge view draws a dhadow or not.
@property (assign,nonatomic) BOOL shadow;
// Indicates whether the badge view should be drawn with a shine
@property (assign,nonatomic) BOOL shine;
// The font to be used for drawing the numbers. NOTE: not all fonts are created equal for this purpose.
// Only "system fonts" should be used.
@property (retain,nonatomic) UIFont* font;
// The color used for the background of the badge.
@property (retain,nonatomic) UIColor* fillColor;
// The color to be used for drawing the stroke around the badge.
@property (retain,nonatomic) UIColor* strokeColor;
// The color to be used for drawing the badge's numbers.
@property (retain,nonatomic) UIColor* textColor;
// How the badge image hould be aligned horizontally in the view.
@property (assign,nonatomic) UITextAlignment alignment;
// Returns the visual size of the badge for the current value. Not the same hing as the size of the view's bounds.
// The badge view bounds should be wider than space needed to draw the badge.
@property (readonly,nonatomic) CGSize badgeSize;
// The number of pixels between the number inside the badge and the stroke around the badge. This value
// is approximate, as the font geometry might effectively slightly increase or decrease the apparent pad.
@property (nonatomic) NSUInteger pad;
@end
//
// MKNumberBadgeView.m
// MKNumberBadgeView
//
// Copyright 2009 Michael F. Kamprath
// michael@claireware.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "MKNumberBadgeView.h"
@interface MKNumberBadgeView ()
//
// private methods
//
- (void)initState;
- (CGPathRef)createBadgePathForTextSize:(CGSize)inSize;
@end
@implementation MKNumberBadgeView
@synthesize value=_value;
@synthesize shadow=_shadow;
@synthesize shine=_shine;
@synthesize font=_font;
@synthesize fillColor=_fillColor;
@synthesize strokeColor=_strokeColor;
@synthesize textColor=_textColor;
@synthesize alignment=_alignment;
@dynamic badgeSize;
@synthesize pad=_pad;
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
// Initialization code
[self initState];
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder
{
if (self = [super initWithCoder:decoder])
{
// Initialization code
[self initState];
}
return self;
}
#pragma mark -- private methods --
- (void)initState;
{
self.opaque = NO;
self.pad = 4;
self.font = [UIFont boldSystemFontOfSize:16];
self.shadow = YES;
self.shine = YES;
self.alignment = UITextAlignmentCenter;
self.fillColor = [UIColor redColor];
self.strokeColor = [UIColor whiteColor];
self.textColor = [UIColor whiteColor];
self.backgroundColor = [UIColor clearColor];
}
- (void)dealloc
{
[_font release];
[_fillColor release];
[_strokeColor release];
[_textColor release];
[super dealloc];
}
- (void)drawRect:(CGRect)rect
{
CGRect viewBounds = self.bounds;
CGContextRef curContext = UIGraphicsGetCurrentContext();
NSString* numberString = [NSString stringWithFormat:@"%d",self.value];
CGSize numberSize = [numberString sizeWithFont:self.font];
CGPathRef badgePath = [self createBadgePathForTextSize:numberSize];
CGRect badgeRect = CGPathGetBoundingBox(badgePath);
badgeRect.origin.x = 0;
badgeRect.origin.y = 0;
badgeRect.size.width = ceil( badgeRect.size.width );
badgeRect.size.height = ceil( badgeRect.size.height );
CGContextSaveGState( curContext );
CGContextSetLineWidth( curContext, 2.0 );
CGContextSetStrokeColorWithColor( curContext, self.strokeColor.CGColor );
CGContextSetFillColorWithColor( curContext, self.fillColor.CGColor );
CGPoint ctm;
switch (self.alignment)
{
default:
case UITextAlignmentCenter:
ctm = CGPointMake( round((viewBounds.size.width - badgeRect.size.width)/2), round((viewBounds.size.height - badgeRect.size.height)/2) );
break;
case UITextAlignmentLeft:
ctm = CGPointMake( 0, round((viewBounds.size.height - badgeRect.size.height)/2) );
break;
case UITextAlignmentRight:
ctm = CGPointMake( (viewBounds.size.width - badgeRect.size.width), round((viewBounds.size.height - badgeRect.size.height)/2) );
break;
}
CGContextTranslateCTM( curContext, ctm.x, ctm.y);
if (self.shadow)
{
CGContextSaveGState( curContext );
CGSize blurSize;
blurSize.width = 0;
blurSize.height = -3;
UIColor* blurColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
CGContextSetShadowWithColor( curContext, blurSize, 4, blurColor.CGColor );
CGContextBeginPath( curContext );
CGContextAddPath( curContext, badgePath );
CGContextClosePath( curContext );
CGContextDrawPath( curContext, kCGPathFillStroke );
CGContextRestoreGState(curContext);
}
CGContextBeginPath( curContext );
CGContextAddPath( curContext, badgePath );
CGContextClosePath( curContext );
CGContextDrawPath( curContext, kCGPathFillStroke );
//
// add shine to badge
//
if (self.shine)
{
CGContextBeginPath( curContext );
CGContextAddPath( curContext, badgePath );
CGContextClosePath( curContext );
CGContextClip(curContext);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat shinyColorGradient[8] = {1, 1, 1, 0.8, 1, 1, 1, 0};
CGFloat shinyLocationGradient[2] = {0, 1};
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace,
shinyColorGradient,
shinyLocationGradient, 2);
CGContextSaveGState(curContext);
CGContextBeginPath(curContext);
CGContextMoveToPoint(curContext, 0, 0);
CGFloat shineStartY = badgeRect.size.height*0.25;
CGFloat shineStopY = shineStartY + badgeRect.size.height*0.4;
CGContextAddLineToPoint(curContext, 0, shineStartY);
CGContextAddCurveToPoint(curContext, 0, shineStopY,
badgeRect.size.width, shineStopY,
badgeRect.size.width, shineStartY);
CGContextAddLineToPoint(curContext, badgeRect.size.width, 0);
CGContextClosePath(curContext);
CGContextClip(curContext);
CGContextDrawLinearGradient(curContext, gradient,
CGPointMake(badgeRect.size.width / 2.0, 0),
CGPointMake(badgeRect.size.width / 2.0, shineStopY),
kCGGradientDrawsBeforeStartLocation);
CGContextRestoreGState(curContext);
CGColorSpaceRelease(colorSpace);
CGGradientRelease(gradient);
}
CGContextRestoreGState( curContext );
CGPathRelease(badgePath);
CGContextSaveGState( curContext );
CGContextSetFillColorWithColor( curContext, self.textColor.CGColor );
CGPoint textPt = CGPointMake( ctm.x + (badgeRect.size.width - numberSize.width)/2 , ctm.y + (badgeRect.size.height - numberSize.height)/2 );
[numberString drawAtPoint:textPt withFont:self.font];
CGContextRestoreGState( curContext );
}
- (CGPathRef)createBadgePathForTextSize:(CGSize)inSize
{
const CGFloat kPi = 3.14159265;
CGFloat arcRadius = ceil((inSize.height+self.pad)/2.0);
CGFloat badgeWidthAdjustment = inSize.width - inSize.height/2.0;
CGFloat badgeWidth = 2.0*arcRadius;
if ( badgeWidthAdjustment > 0.0 )
{
badgeWidth += badgeWidthAdjustment;
}
else
{
badgeWidthAdjustment = 0;
}
CGMutablePathRef badgePath = CGPathCreateMutable();
CGPathMoveToPoint( badgePath, NULL, arcRadius, 0 );
CGPathAddArc( badgePath, NULL, arcRadius, arcRadius, arcRadius, 3.0*kPi/2.0, kPi/2.0, YES);
CGPathAddLineToPoint( badgePath, NULL, badgeWidth-arcRadius, 2.0*arcRadius);
CGPathAddArc( badgePath, NULL, badgeWidth-arcRadius, arcRadius, arcRadius, kPi/2.0, 3.0*kPi/2.0, YES);
CGPathAddLineToPoint( badgePath, NULL, arcRadius, 0 );
return badgePath;
}
#pragma mark -- property methods --
- (void)setValue:(NSUInteger)inValue
{
_value = inValue;
[self setNeedsDisplay];
}
- (CGSize)badgeSize
{
NSString* numberString = [NSString stringWithFormat:@"%d",self.value];
CGSize numberSize = [numberString sizeWithFont:self.font];
CGPathRef badgePath = [self createBadgePathForTextSize:numberSize];
CGRect badgeRect = CGPathGetBoundingBox(badgePath);
badgeRect.origin.x = 0;
badgeRect.origin.y = 0;
badgeRect.size.width = ceil( badgeRect.size.width );
badgeRect.size.height = ceil( badgeRect.size.height );
CGPathRelease(badgePath);
return badgeRect.size;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment