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

转载 2015年11月18日 14:56:31

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

By    | 2014/05/12 

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

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

  转载请注明本文章出处:http://www.androiddev.net/

  在上一篇博客手把手教你编写cocoa Calendar控件之DayView(h ttp://www.androiddev.net/)中编者讲述了如何编写最基本的元件DayView,本篇博客将讲述如何编写MonthView。MonthView是由42个DayView的对象构成,结构为6行7列图形界面如下所示:

Screen Shot 2014-05-09 at 5.19.28 PM

如何将这么多的DayView放入MonthView中呢?在NSView中有一个subviews的property,在MonthView中调用 - (NSArray *)subviews 返回的是一个NSArray,因此当我们要设置subviews的时候也要传入一个NSArray的对象。接下来我们实验性的填写以下subviews。首先在项目中新建一个CBMonthView类在CBMonthView.m中添加方法:

- (void) awakeFromNib{
    [super awakeFromNib];
    NSMutableArray *array = [[NSMutableArray alloc] init];
    double width = self.bounds.size.width/7,height = self.bounds.size.height/6;
    for(int i=5,count=1;i!=0;i--){
        for(int j=0;j<7;j++,count++){
            CBDayView *dayView = [[CBDayView alloc] initWithFrame:NSMakeRect(j*width, i*height, width, height)];
            dayView.day = [NSString stringWithFormat:@"%d";,count];
            [array addObject:dayView];
        }
    }
    [self setSubviews:array];
}

恩还有一个地方需要修改,在CBDayView.m中定义initWithFrame:(NSRect)frame方法:

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self initialize];
    }
    return self;
}

 

在MainMenu.xib中像添加CBDayView那样添加CBMonthView(详细参见手把手教你编写cocoa Calendar控件之DayView)之后运行程序我们便得到如下结果:

Screen Shot 2014-05-12 at 8.15.59 PM

样子是出来了但是该视图并不是像日历,别着急接下来我们还要做很多工作让该视图看起来像日历。

要让CBMonthView看起来更像日历那么就要解决给定月份的天数,当前日期(年月日)以及判断给定年月日是周几的问题。为了解决这三个问题笔者使用NSDate、NSDateComponents、NSCalendar这么三个类来解决这三个问题。

1、判断给定月份的天数:

- (int)getMonthLengthWithMonth:(int)month withYear:(int)year{
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setMonth:month];
    [comps setYear:year];
    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *date = [gregorian dateFromComponents:comps];
    NSRange days = [gregorian rangeOfUnit:NSDayCalendarUnit
                                   inUnit:NSMonthCalendarUnit
                                  forDate:date];
    return (int)days.length;
}

2、判断当前日期

- (NSDictionary *)getCurrentMonthDayYear{
    NSCalendar *calendar = [[NSLocale currentLocale] objectForKey:NSLocaleCalendar];
    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit | NSWeekdayCalendarUnit | NSWeekCalendarUnit;
    NSDate *date = [NSDate date];
    NSDateComponents *comps = [calendar components:unitFlags fromDate:date];
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    [dict setObject:[NSNumber numberWithInteger:comps.month] forKey:@"month"];
    [dict setObject:[NSNumber numberWithInteger:comps.day] forKey:@"day"];
    [dict setObject:[NSNumber numberWithInteger:comps.year] forKey:@"year"];
    return dict;
}

3、判断给定日期是周几

- (int)getWeekdayWithMonth:(int)month withDay:(int)day withYear:(int)year{
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setDay:day];
    [comps setMonth:month];
    [comps setYear:year];
    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *date = [gregorian dateFromComponents:comps];
    NSDateComponents *weekdayComponents =
    [gregorian components:NSWeekdayCalendarUnit fromDate:date];
    return (int)[weekdayComponents weekday];
}

为了测试一下三个函数我们在awakeFromNib方法总添加下面语句:

    NSLog(@"%d",[self getMonthLengthWithMonth:5 withYear:2014]);
    NSDictionary *dict = [self getCurrentMonthDayYear];
    NSLog(@"%d年 %d月 %d日",[[dict objectForKey:@"year"] intValue],[[dict objectForKey:@"month"] intValue],[[dict objectForKey:@"day"] intValue]);
    NSLog(@"%d",[self getWeekdayWithMonth:5 withDay:12 withYear:2014]);

得到如下结果:

Screen Shot 2014-05-12 at 8.45.01 PM

好接下来我们要让可爱的CBMonthView显示本月份的天数并标记处本天,我们在CBMonthView.m中添加方法 updateCalendarWithMonth:(int)month withYear:(int)year具体代码如下:

- (void)updateCalendarWithMonth:(int)month withYear:(int)year{
    NSMutableArray *views = [[NSMutableArray alloc] init];
    double height = self.bounds.size.height,width = self.bounds.size.width;
    double w = width/7,h = height/6;
    int firstday = [self getWeekdayWithMonth:month withDay:1 withYear:year];
    int monthLength = [self getMonthLengthWithMonth:month withYear:year];
    NSDictionary *currentDate = [self getCurrentMonthDayYear];
    for(int day = 1,i=5,weekday = firstday;day != monthLength ;i--){
        for(;weekday != 7&&day != monthLength;weekday++,day++){
            CBDayView *dayView = [[CBDayView alloc] initWithFrame:CGRectMake(w*(weekday-1), h*i, w, h)];
            dayView.day = [NSString stringWithFormat:@"%d",day];
            dayView.state = 0;
            if([[currentDate objectForKey:@"day"] intValue]==day&&year==[[currentDate objectForKey:@"year"] intValue]&&month==[[currentDate objectForKey:@"month"] intValue]){
                [dayView drawCirleInRect:YES color:nil];
            }
            [views addObject:dayView];
        }
        weekday = 1;
    }
    [self setSubviews:views];
}

添加完成updateCalendarWithMonth:(int)month withYear:(int)year后我们在改写一下awakeFromNib方法:

- (void) awakeFromNib{
    [super awakeFromNib];
    [self updateCalendarWithMonth:5 withYear:2014];
}

运行后得到下列结果图:
Screen Shot 2014-05-12 at 8.54.40 PM
现在的CBMonthView看下来想那么回事了但是距离我们的最终目标:
Screen Shot 2014-05-09 at 5.19.28 PM
还是有一定的差距。我们最终的CBMonthView中显示了当前月份前一个月和后一个月的信息,为了让我们的CBMonthView达到这样的效果我们还需要在updateCalendarWithMonth:(int)month withYear:(int)yeari添加一些代码:

- (void)updateCalendarWithMonth:(int)month withYear:(int)year{
    NSMutableArray *views = [[NSMutableArray alloc] init];
    double height = self.bounds.size.height,width = self.bounds.size.width;
    double w = width/7,h = height/6;
    int firstday = [self getWeekdayWithMonth:month withDay:1 withYear:year];
    int monthLength = [self getMonthLengthWithMonth:month withYear:year];
    NSDictionary *currentDate = [self getCurrentMonthDayYear];
    for(int day = 1,i=5,weekday = firstday;day <= monthLength ;i--){
        for(;weekday <= 7&&day <= monthLength;weekday++,day++){
            CBDayView *dayView = [[CBDayView alloc] initWithFrame:CGRectMake(w*(weekday-1), h*i, w, h)];
            dayView.day = [NSString stringWithFormat:@"%d",day];
            dayView.state = 0;
            if([[currentDate objectForKey:@"day"] intValue]==day&&year==[[currentDate objectForKey:@"year"] intValue]&&month==[[currentDate objectForKey:@"month"] intValue]){
                [dayView drawCirleInRect:YES color:nil];
            }
            [views addObject:dayView];
        }
        weekday = 1;
    }
    int perious=0;
    if(month-1<=0)
        perious = [self getMonthLengthWithMonth:1 withYear:year-1]-firstday+2;
    else
        perious = [self getMonthLengthWithMonth:month-1 withYear:year]-firstday+2;

    for(int i=0;i<firstday-1;i++,perious++){
        CBDayView *dayView = [[CBDayView alloc] initWithFrame:CGRectMake(w*i, h*5, w, h)];
        [dayView setDayFontColor:[NSColor colorWithCalibratedRed:184.0/225 green:184.0/225 blue:184.0/225 alpha:1]];
        [dayView setDay:[NSString stringWithFormat:@"%d",perious]];
        dayView.state = -1;
        [views addObject:dayView];
    }
    int nextMonthDays = 43-monthLength-firstday;
    int rawNum = nextMonthDays/7;
    int leastDays = monthLength-(8-firstday);

    if(nextMonthDays%7)
        rawNum++;
    for(int i=4-leastDays/7,count=1,j=leastDays%7;i>=0;i--){
        for(;j<7;j++,count++){
            CBDayView *dayView = [[CBDayView alloc] initWithFrame:CGRectMake(w*j, h*i, w, h)];
            [dayView setDayFontColor:[NSColor colorWithCalibratedRed:184.0/225 green:184.0/225 blue:184.0/225 alpha:1]];
            [dayView setDay:[NSString stringWithFormat:@"%d",count]];
            dayView.state = 1;
            [views addObject:dayView];
        }
        j=0;
    }

    [self setSubviews:views];
}

运行一下:

Screen Shot 2014-05-12 at 9.01.33 PM

我们实现了最终的目标。

接下来完善一下CBMonthView并附上最终的代码:

#import <Cocoa/Cocoa.h>

@interface CBMonthView : NSView

@property (nonatomic, strong)NSDictionary *currentDate;

- (void)updateCalendarWithMonth:(int)month withYear:(int)year;

@end
//CBMonthView.m
#import "CBMonthView.h"
#import "CBDayView.h"

@implementation CBMonthView

- (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)awakeFromNib{
    [self initialize];
}

- (void)initialize{
    NSDictionary *currentDate = [self getCurrentMonthDayYear];
    self.currentDate = currentDate;
    [self updateCalendarWithMonth:[[currentDate objectForKey:@"month"] intValue] withYear:[[currentDate objectForKey:@"year"] intValue]];
}

- (void)updateCalendarWithMonth:(int)month withYear:(int)year{
    NSMutableArray *views = [[NSMutableArray alloc] init];
    double height = self.bounds.size.height,width = self.bounds.size.width;
    double w = width/7,h = height/6;
    int firstday = [self getWeekdayWithMonth:month withDay:1 withYear:year];
    int monthLength = [self getMonthLengthWithMonth:month withYear:year];
    NSDictionary *currentDate = [self getCurrentMonthDayYear];
    for(int day = 1,i=5,weekday = firstday;day <= monthLength ;i--){
        for(;weekday <= 7&&day <= monthLength;weekday++,day++){
            CBDayView *dayView = [[CBDayView alloc] initWithFrame:CGRectMake(w*(weekday-1), h*i, w, h)];
            dayView.day = [NSString stringWithFormat:@"%d",day];
            dayView.state = 0;
            if([[currentDate objectForKey:@"day"] intValue]==day&&year==[[currentDate objectForKey:@"year"] intValue]&&month==[[currentDate objectForKey:@"month"] intValue]){
                [dayView drawCirleInRect:YES color:nil];
            }
            [views addObject:dayView];
        }
        weekday = 1;
    }
    int perious=0;
    if(month-1<=0)
        perious = [self getMonthLengthWithMonth:1 withYear:year-1]-firstday+2;
    else
        perious = [self getMonthLengthWithMonth:month-1 withYear:year]-firstday+2;

    for(int i=0;i<firstday-1;i++,perious++){
        CBDayView *dayView = [[CBDayView alloc] initWithFrame:CGRectMake(w*i, h*5, w, h)];
        [dayView setDayFontColor:[NSColor colorWithCalibratedRed:184.0/225 green:184.0/225 blue:184.0/225 alpha:1]];
        [dayView setDay:[NSString stringWithFormat:@"%d",perious]];
        dayView.state = -1;
        [views addObject:dayView];
    }
    int nextMonthDays = 43-monthLength-firstday;
    int rawNum = nextMonthDays/7;
    int leastDays = monthLength-(8-firstday);

    if(nextMonthDays%7)
        rawNum++;
    for(int i=4-leastDays/7,count=1,j=leastDays%7;i>=0;i--){
        for(;j<7;j++,count++){
            CBDayView *dayView = [[CBDayView alloc] initWithFrame:CGRectMake(w*j, h*i, w, h)];
            [dayView setDayFontColor:[NSColor colorWithCalibratedRed:184.0/225 green:184.0/225 blue:184.0/225 alpha:1]];
            [dayView setDay:[NSString stringWithFormat:@"%d",count]];
            dayView.state = 1;
            [views addObject:dayView];
        }
        j=0;
    }

    [self setSubviews:views];
}

- (int)getMonthLengthWithMonth:(int)month withYear:(int)year{
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setMonth:month];
    [comps setYear:year];
    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *date = [gregorian dateFromComponents:comps];
    NSRange days = [gregorian rangeOfUnit:NSDayCalendarUnit
                                   inUnit:NSMonthCalendarUnit
                                  forDate:date];
    return (int)days.length;
}

- (NSDictionary *)getCurrentMonthDayYear{
    NSCalendar *calendar = [[NSLocale currentLocale] objectForKey:NSLocaleCalendar];
    unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit | NSWeekdayCalendarUnit | NSWeekCalendarUnit;
    NSDate *date = [NSDate date];
    NSDateComponents *comps = [calendar components:unitFlags fromDate:date];
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    [dict setObject:[NSNumber numberWithInteger:comps.month] forKey:@"month"];
    [dict setObject:[NSNumber numberWithInteger:comps.day] forKey:@"day"];
    [dict setObject:[NSNumber numberWithInteger:comps.year] forKey:@"year"];
    return dict;
}

- (int)getWeekdayWithMonth:(int)month withDay:(int)day withYear:(int)year{
    NSDateComponents *comps = [[NSDateComponents alloc] init];
    [comps setDay:day];
    [comps setMonth:month];
    [comps setYear:year];
    NSCalendar *gregorian = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSGregorianCalendar];
    NSDate *date = [gregorian dateFromComponents:comps];
    NSDateComponents *weekdayComponents =
    [gregorian components:NSWeekdayCalendarUnit fromDate:date];
    return (int)[weekdayComponents weekday];
}

@end

Screen Shot 2014-05-09 at 5.20.39 PM

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

本文出处:码农日记http://www.androiddev.net/

作者:翟帅

相关文章推荐

手把手教你写Js日期时间选择器(3)-让控件滑动起来

本节为大家讲解怎么让控件滑动起来,这里需要用到一个第三方库IScroll.js.先奉上链接地址:IScroll githubIScroll 网站IScroll介绍IScroll是一个高性能,体积小,无...

自定义view学习-手把手教你制作一个可扩展日历控件

来看看效果图先,手把手教你实现一个简易,但高扩展度的日历控件,可自由扩展成签到,单选,多选日期。 首先我们来分析实现思路。对于上图的效果,很明显是一个6x7的表格。 我们可以两个for循环控制...

手把手教你制作 Windows8 应用程序内部的 hubtile (动态瓷砖控件) MetroStyle

在metro 风格中 动态磁贴是他的精髓 在wp7 的开发中 我们可以使用hubtile 来制作类似效果 但是在 win8 中并不具备这个功能, 下面我们来通过扩展GridViewIt...

Android自定义控件——手把手教你实现SlidingMenu(二)

上篇回顾:上篇中创建了项目的基本组成,并且成功的把Activity的contentView  set  给了我们自定义的RelativeLayout中的SlidingMenuAbove 系列二  ...

手把手教你制作验证码控件

好了今天我们在做一个验证码控件. 首先先说要做的是什么我们要做的一个控件,可以从工具栏拖出来的那种.验证码控件首先想到的是什么,好吧.我首先想到的是Image服务器控件。所以就先继承imag...

手把手教你BCGControlBar MFC界面控件“起航”技巧(文章转载自:慧都控件网)

BCGControlBar是全球最大的MFC控件套包,由微软指定合作控件开发商BCGSoft公司开发,主要用于构建类似Microsoft® Office 2000/XP/2003/2007/2010、...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)