手把手教你编写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
给个源码下载链接呗
我擦,屌丝很牛逼啊
逼格有点高,略懂,大神!