Created
February 21, 2012 06:35
-
-
Save wujun/1874350 to your computer and use it in GitHub Desktop.
custom badge from Michael F. Kamprath
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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