手把手教你编写cocoa Calendar控件之DayView

By | 2014/05/09

手把手教你编写cocoa Calendar控件之DayView

项目地址:https://github.com/zhaishuai/CBCalendar

最近在一个MAC项目的开发中需要使用calendar控件,但苹果提供的NSDatePicker功能又太少而且也很少有较好的开源控件,所以笔者决定自己编写一个可深度定制的Calendar。

Calendar控件表面看上去比较容易编写其实不然,要编写一个国际化的calendar控件还是有很多地方难以解决的。比如不同国家地区的时区不同、语言不同、日期格式不同等问题使得编写该控件的难度增加了不少。既然有一定的难度我们就采用化整为零的方法来步步解决该难题。

首先我们将该日历分为三部分:

1.DayView

Screen Shot 2014-05-09 at 5.18.26 PM

2.MonthView

Screen Shot 2014-05-09 at 5.19.28 PM

3.headView

Screen Shot 2014-05-09 at 5.19.58 PM

 

综合效果图:

Screen Shot 2014-05-09 at 5.20.39 PM

 

接下来我们分析一下DayView的功能:

1.DayView设置并显示一个日期。

2.显示特殊标注。

3.能够根据bounds自动调整字体的大小。

4.自动调整文字的位置使文字保持居中。

5.能够改变字体的颜色。

6.能够改变DayView的背景颜色。

7.能够改变DayView的边框颜色。

8.点击该视图能够触发事件。

步骤:

1.创建项目

Screen Shot 2014-05-09 at 5.23.30 PM

<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

Screen Shot 2014-05-09 at 5.25.38 PM

Screen Shot 2014-05-09 at 5.26.16 PM

Screen Shot 2014-05-09 at 5.27.23 PM

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,打开窗口

Screen Shot 2014-05-09 at 6.17.44 PM

选择view Controller控件

Screen Shot 2014-05-09 at 6.19.28 PM

将其拖住拽至

Screen Shot 2014-05-09 at 6.21.23 PM

接下来在窗口中添加一个Custom View控件至窗口

Screen Shot 2014-05-09 at 6.22.21 PM

选择刚刚添加的viewController控件,按住右键不放将箭头指向新添加的Custom View。然后单击弹出菜单中的view

Screen Shot 2014-05-09 at 6.23.48 PM

选中Custom View控件后Screen Shot 2014-05-09 at 6.42.01 PM点击图中的蓝色按钮,再如后图所示填写Screen Shot 2014-05-09 at 6.42.51 PM

好我们运行一下会看到如下效果图:

Screen Shot 2014-05-09 at 6.44.24 PM

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];

最后的运行效果如下所示:

Screen Shot 2014-05-09 at 7.23.18 PM

完整代码:

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

3 thoughts on “手把手教你编写cocoa Calendar控件之DayView

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.