手把手教你编写cocoa Calendar控件之DayView
项目地址:https://github.com/zhaishuai/CBCalendar
最近在一个MAC项目的开发中需要使用calendar控件,但苹果提供的NSDatePicker功能又太少而且也很少有较好的开源控件,所以笔者决定自己编写一个可深度定制的Calendar。
Calendar控件表面看上去比较容易编写其实不然,要编写一个国际化的calendar控件还是有很多地方难以解决的。比如不同国家地区的时区不同、语言不同、日期格式不同等问题使得编写该控件的难度增加了不少。既然有一定的难度我们就采用化整为零的方法来步步解决该难题。
首先我们将该日历分为三部分:
1.DayView
![]()
2.MonthView

3.headView
综合效果图:
接下来我们分析一下DayView的功能:
1.DayView设置并显示一个日期。
2.显示特殊标注。
3.能够根据bounds自动调整字体的大小。
4.自动调整文字的位置使文字保持居中。
5.能够改变字体的颜色。
6.能够改变DayView的背景颜色。
7.能够改变DayView的边框颜色。
8.点击该视图能够触发事件。
步骤:
1.创建项目
<img class="alignnone size-medium wp-image-510" src="http://www.androiddev.net/wp-content/uploads/2014/05/Screen-Shot-2014-05-09-at-5 Continue Reading.24.42-PM-300×220.png” alt=”Screen Shot 2014-05-09 at 5.24.42 PM” width=”300″ height=”220″ srcset=”https://www.androiddev.net/wp-content/uploads/2014/05/Screen-Shot-2014-05-09-at-5.24.42-PM-300×220.png 300w, https://www.androiddev.net/wp-content/uploads/2014/05/Screen-Shot-2014-05-09-at-5.24.42-PM.png 537w” sizes=”(max-width: 300px) 100vw, 300px” />
2.创建CBDayView
3.绘制矩形
我们在CBDayView.m文件中增加一个绘制矩形的函数:
//CBDayView.h
@interface CBDayView : NSView
@property (nonatomic,strong)NSColor *rectBackgroundColor;
@property (nonatomic,strong)NSColor *rectBoundColor;
@end
//CBDayView.m
#import "CBDayView.h"
@implementation CBDayView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
}
return self;
}</pre>
<pre>- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
- (void)drawRectangle:(NSRect)rect context:(NSGraphicsContext*)context{
[context saveGraphicsState];
[self.rectBackgroundColor setFill];
[self.rectBoundColor setStroke];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.02];
NSRectFill(rect);
[NSBezierPath strokeRect:rect];
[context restoreGraphicsState];
}
@end
接下来在- (void)drawRect:(NSRect)dirtyRect方法中调用- (void)drawRectangle:(NSRect)rect context:(NSGraphicsContext*)context
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
NSGraphicsContext* context = [NSGraphicsContext currentContext];
[self drawRectangle:
CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)
context:context];
}
添加initialize方法,并在其中添加与初始化相关的代码,如颜色的赋值等。然后再在- (void)awakeFromNib方法中调用initialize
- (void)awakeFromNib{
[self initialize];
}
- (void)initialize{
self.rectBoundColor = [NSColor colorWithCalibratedRed:217.0/255 green:217.0/255 blue:217.0/255 alpha:1];
self.rectBackgroundColor = [NSColor colorWithCalibratedRed:251.0/255 green:251.0/255 blue:251.0/255 alpha:1];
}
4.编辑界面
打开MainMenu.xib,打开窗口
选择view Controller控件
将其拖住拽至
接下来在窗口中添加一个Custom View控件至窗口
选择刚刚添加的viewController控件,按住右键不放将箭头指向新添加的Custom View。然后单击弹出菜单中的view
选中Custom View控件后
点击图中的蓝色按钮,再如后图所示填写
。
好我们运行一下会看到如下效果图:
5.添加文字
在DayView.h文件中的interface中添加属性
@property (nonatomic,strong)NSString *day;
@property (nonatomic,strong)NSColor *dayFontColor;
再在drawRect中添加绘制文字的代码
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont boldSystemFontOfSize:self.bounds.size.height*0.3], NSFontAttributeName,self.dayFontColor, NSForegroundColorAttributeName, nil];
NSAttributedString * currentText=[[NSAttributedString alloc] initWithString:self.day attributes: attributes];
[currentText drawAtPoint:NSMakePoint(0+(self.bounds.size.width-self.bounds.size.height*0.3*0.5*self.day.length)/2, 0+(self.bounds.size.height-self.bounds.size.height*0.3)/2)];
在initialize中添加字体默认颜色,以及CBDayView中默认的文字
self.dayFontColor = [NSColor colorWithCalibratedRed:119.0/255 green:119.0/255 blue:119.0/255 alpha:1];
self.day = @"1";
接下来重写属性的setter方法,使得当属性改变时程序自动重绘界面。
- (void)setDay:(NSString *)day{
_day = day;
[self setNeedsDisplay:YES];
}
- (void)setRectBackgroundColor:(NSColor *)rectBackgroundColor{
_rectBackgroundColor = rectBackgroundColor;
[self setNeedsDisplay:YES];
}
- (void)setRectBoundColor:(NSColor *)rectBoundColor{
_rectBoundColor = rectBoundColor;
[self setNeedsDisplay:YES];
}
- (void)setDayFontColor:(NSColor *)dayFontColor{
_dayFontColor = dayFontColor;
[self setNeedsDisplay:YES];
}
6.绘制圆以及点
在CBDayView.h中添加
@property int state;
/**
* state YES mean you can draw a circle in the rect then set a color;
* state NO mean you can not draw a circle int the rect then you should set color to nil;
*/
– (void)drawCirleInRect:(BOOL)state color:(NSColor *)color;
/**
* state YES mean you can draw a point in the rect then set a color;
* state NO mean you can not draw a point int the rect then you should set color to nil;
*/
– (void)drawPointInRect:(BOOL)state color:(NSColor *)color;
方法。
在CBDayView.m中添加
– (void)addCircleToFontWithContext:(NSGraphicsContext *)context
– (void)addPointWithContext:(NSGraphicsContext *)context
方法来绘制圆及点
@implementation CBDayView{
NSColor *circleColor;
NSColor *pointColor;
NSColor *tempBackGroundColor;
BOOL addCircleToRect;
BOOL addPoint;
}
- (void)addCircleToFontWithContext:(NSGraphicsContext *)context{
[context saveGraphicsState];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.05];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[circleColor setStroke];
[thePath appendBezierPathWithOvalInRect:CGRectMake(self.bounds.size.width*0.1, self.bounds.size.height*0.1, self.bounds.size.width*0.8, self.bounds.size.height*0.8)];
[thePath stroke];
[context restoreGraphicsState];
}
- (void)addPointWithContext:(NSGraphicsContext *)context{
[context saveGraphicsState];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.2];
[pointColor setFill];
[thePath appendBezierPathWithOvalInRect:CGRectMake(self.bounds.size.width/2-self.bounds.size.width*0.04, self.bounds.size.height*0.2, self.bounds.size.width*0.1, self.bounds.size.height*0.1)];
[thePath fill];
[context restoreGraphicsState];
}
- (void)addCircleToFontWithContext:(NSGraphicsContext *)context{
[context saveGraphicsState];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.05];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[circleColor setStroke];
[thePath appendBezierPathWithOvalInRect:CGRectMake(self.bounds.size.width*0.1, self.bounds.size.height*0.1, self.bounds.size.width*0.8, self.bounds.size.height*0.8)];
[thePath stroke];
[context restoreGraphicsState];
}
- (void)addPointWithContext:(NSGraphicsContext *)context{
[context saveGraphicsState];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.2];
[pointColor setFill];
[thePath appendBezierPathWithOvalInRect:CGRectMake(self.bounds.size.width/2-self.bounds.size.width*0.04, self.bounds.size.height*0.2, self.bounds.size.width*0.1, self.bounds.size.height*0.1)];
[thePath fill];
[context restoreGraphicsState];
}
接下来在awakFromNib中添加测试代码
在initialize方法中添加圆圈以及点的默认颜色
//添加在initialize方法中
circleColor = [NSColor colorWithCalibratedRed:225.0/255 green:66.0/255 blue:66.0/255 alpha:1];
pointColor = [NSColor colorWithCalibratedRed:92.0/255 green:174.0/255 blue:8.0/255 alpha:1];
- (void)awakeFromNib{
[self initialize];
[self drawPointInRect:YES color:nil];
[self drawCirleInRect:YES color:nil];
}
最后一步就是在drawRect方法中添加绘制圆和点的方法
if(addCircleToRect )
[self addCircleToFontWithContext:context];
if(addPoint )
[self addPointWithContext:context];
最后的运行效果如下所示:
完整代码:
CBDayView.h:
#import <Cocoa/Cocoa.h> @class CBDayView; @protocol dayClick <NSObject> - (void)clickDay:(CBDayView *)dayView; @end @interface CBDayView : NSView @property (nonatomic, weak)id<dayClick> delegate; @property (nonatomic,strong)NSString *day; //-1 means last month; 0 means current month; 1 means next month; @property int state; @property (nonatomic,strong)NSColor *rectBackgroundColor; @property (nonatomic,strong)NSColor *rectBoundColor; @property (nonatomic,strong)NSColor *dayFontColor; /** * state YES mean you can draw a circle in the rect then set a color; * state NO mean you can not draw a circle int the rect then you should set color to nil; */ - (void)drawCirleInRect:(BOOL)state color:(NSColor *)color; /** * state YES mean you can draw a point in the rect then set a color; * state NO mean you can not draw a point int the rect then you should set color to nil; */ - (void)drawPointInRect:(BOOL)state color:(NSColor *)color; @end
CBDayVIew.m
#import "CBDayView.h"
@implementation CBDayView{
NSColor *circleColor;
NSColor *pointColor;
NSColor *tempBackGroundColor;
BOOL addCircleToRect;
BOOL addPoint;
}
#pragma mark - initalize
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initialize];
}
return self;
}
- (id)init{
self = [super init];
if(self){
[self initialize];
}
return self;
}
- (void)initialize{
self.rectBoundColor = [NSColor colorWithCalibratedRed:217.0/255 green:217.0/255 blue:217.0/255 alpha:1];
self.rectBackgroundColor = [NSColor colorWithCalibratedRed:251.0/255 green:251.0/255 blue:251.0/255 alpha:1];
self.dayFontColor = [NSColor colorWithCalibratedRed:119.0/255 green:119.0/255 blue:119.0/255 alpha:1];
circleColor = [NSColor colorWithCalibratedRed:225.0/255 green:66.0/255 blue:66.0/255 alpha:1];
pointColor = [NSColor colorWithCalibratedRed:92.0/255 green:174.0/255 blue:8.0/255 alpha:1];
self.day = @"1";
}
- (void)awakeFromNib{
[super awakeFromNib];
}
#pragma mark - setter method
- (void)setDay:(NSString *)day{
_day = day;
[self setNeedsDisplay:YES];
}
- (void)setRectBackgroundColor:(NSColor *)rectBackgroundColor{
_rectBackgroundColor = rectBackgroundColor;
[self setNeedsDisplay:YES];
}
- (void)setRectBoundColor:(NSColor *)rectBoundColor{
_rectBoundColor = rectBoundColor;
[self setNeedsDisplay:YES];
}
- (void)setDayFontColor:(NSColor *)dayFontColor{
_dayFontColor = dayFontColor;
[self setNeedsDisplay:YES];
}
#pragma mark - draw method
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
NSGraphicsContext* context = [NSGraphicsContext currentContext];
[self drawRectangle:
CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height)
context:context];
// NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:@"Helvetica" size:self.bounds.size.height*0.3], NSFontAttributeName,self.dayFontColor, NSForegroundColorAttributeName, nil];
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont boldSystemFontOfSize:self.bounds.size.height*0.3], NSFontAttributeName,self.dayFontColor, NSForegroundColorAttributeName, nil];
NSAttributedString * currentText=[[NSAttributedString alloc] initWithString:self.day attributes: attributes];
[currentText drawAtPoint:NSMakePoint(0+(self.bounds.size.width-self.bounds.size.height*0.3*0.5*self.day.length)/2, 0+(self.bounds.size.height-self.bounds.size.height*0.3)/2)];
if(addCircleToRect )
[self addCircleToFontWithContext:context];
if(addPoint )
[self addPointWithContext:context];
//NSLog(@"redraw");
}
- (void)drawRectangle:(NSRect)rect context:(NSGraphicsContext*)context{
[context saveGraphicsState];
[self.rectBackgroundColor setFill];
[self.rectBoundColor setStroke];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.02];
NSRectFill(rect);
[NSBezierPath strokeRect:rect];
[context restoreGraphicsState];
}
- (void)addCircleToFontWithContext:(NSGraphicsContext *)context{
[context saveGraphicsState];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.05];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[circleColor setStroke];
[thePath appendBezierPathWithOvalInRect:CGRectMake(self.bounds.size.width*0.1, self.bounds.size.height*0.1, self.bounds.size.width*0.8, self.bounds.size.height*0.8)];
[thePath stroke];
[context restoreGraphicsState];
}
- (void)addPointWithContext:(NSGraphicsContext *)context{
[context saveGraphicsState];
NSBezierPath* thePath = [NSBezierPath bezierPath];
[NSBezierPath setDefaultLineWidth:self.bounds.size.height*0.2];
[pointColor setFill];
[thePath appendBezierPathWithOvalInRect:CGRectMake(self.bounds.size.width/2-self.bounds.size.width*0.04, self.bounds.size.height*0.2, self.bounds.size.width*0.1, self.bounds.size.height*0.1)];
[thePath fill];
[context restoreGraphicsState];
}
- (void)drawCirleInRect:(BOOL)state color:(NSColor *)color{
addCircleToRect = state;
//if(!state)
//NSLog(@"hello");
if(color!=nil)
circleColor = color;
[self setNeedsDisplay:YES];
}
- (void)drawPointInRect:(BOOL)state color:(NSColor *)color{
addPoint = state;
if(color!=nil)
pointColor = color;
[self setNeedsDisplay:YES];
}
#pragma mark - mouse event
- (void)mouseDown:(NSEvent *)theEvent{
//tempBackGroundColor = self.rectBackgroundColor;
self.alphaValue = 0.3;
[self setNeedsDisplay:YES];
//NSLog(@"mouseDown");
}
- (void)mouseUp:(NSEvent *)theEvent{
//self.rectBackgroundColor = tempBackGroundColor;
self.alphaValue = 1;
[self setNeedsDisplay:YES];
[self.delegate clickDay:self];
}
@end












给个源码下载链接呗
我擦,屌丝很牛逼啊
逼格有点高,略懂,大神!