在iOS上实现一个简单的日历控件

原创 2014年03月24日 19:33:44

近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表。

不过再怎么复杂的控件,也是由基础的零配件组装起来的,这里最基本的就是日历控件。

先上图:


从图中可以看出日历控件就是由一个个小方块组成的,每一行有7个小方块,分别表示一周的星期天到星期六。

给定一个月份,我们首先需要知道这个月有多少周。那么如何确定一个月有多少周呢?

我是这么想的,在NSDate上做扩展:

@interface NSDate (WQCalendarLogic)

0. 首先需要知道这个月有多少天:

- (NSUInteger)numberOfDaysInCurrentMonth
{
    // 频繁调用 [NSCalendar currentCalendar] 可能存在性能问题
    return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;
}

1. 确定这个月的第一天是星期几。这样就能知道给定月份的第一周有几天:

- (NSDate *)firstDayOfCurrentMonth
{
    NSDate *startDate = nil;
    BOOL ok = [[NSCalendar currentCalendar] rangeOfUnit:NSMonthCalendarUnit startDate:&startDate interval:NULL forDate:self];
    NSAssert1(ok, @"Failed to calculate the first day of the month based on %@", self);
    return startDate;
}

- (NSUInteger)weeklyOrdinality
{
    return [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:self];
}

2. 减去第一周的天数,剩余天数除以7,得到倍数和余数:

- (NSUInteger)numberOfWeeksInCurrentMonth
{
    NSUInteger weekday = [[self firstDayOfCurrentMonth] weeklyOrdinality];
    
    NSUInteger days = [self numberOfDaysInCurrentMonth];
    NSUInteger weeks = 0;
    
    if (weekday > 1) {
        weeks += 1, days -= (7 - weekday + 1);
    }
    
    weeks += days / 7;
    weeks += (days % 7 > 0) ? 1 : 0;
    
    return weeks;
}

到这里,就可以知道一个月有多少行多少列,从而计算出需要多少个小方块来展示:

@interface WQCalendarTileView : UIView

这些小方块用一个大方块来承载:

@interface WQCalendarGridView : UIView

@property (nonatomic, weak) id<WQCalendarGridViewDataSource> dataSource;
@property (nonatomic, weak) id<WQCalendarGridViewDelegate> delegate;

- (void)reloadData;

和UITableView类似,当WQCalendarGridView调用reloadData接口时,会开始进行布局。而布局所需要的信息由dataSource和delegate提供:

@class WQCalendarGridView;

@protocol WQCalendarGridViewDataSource <NSObject>

@required

- (NSUInteger)numberOfRowsInGridView:(WQCalendarGridView *)gridView;

- (WQCalendarTileView *)gridView:(WQCalendarGridView *)gridView tileViewForRow:(NSUInteger)row column:(NSUInteger)column;

@optional

- (CGFloat)heightForRowInGridView:(WQCalendarGridView *)gridView;

@end

@protocol WQCalendarGridViewDelegate <NSObject>

- (void)gridView:(WQCalendarGridView *)gridView didSelectAtRow:(NSUInteger)row column:(NSUInteger)column;

@end

每一行的高度,heightForRow,我比较倾向于由dataSource提供 :)

第一个dataSource方法上面已经可以计算出来了,第二个dataSource方法需要对每一个tile进行配置,比如非当前月的以灰色展示,那么我们就需要知道当前月展示中包含的 上个月残留部分,和 下个月的开头部分:

#pragma mark - method to calculate days in previous, current and the following month.

- (void)calculateDaysInPreviousMonthWithDate:(NSDate *)date
{
    NSUInteger weeklyOrdinality = [[date firstDayOfCurrentMonth] weeklyOrdinality];
    NSDate *dayInThePreviousMonth = [date dayInThePreviousMonth];
    
    NSUInteger daysCount = [dayInThePreviousMonth numberOfDaysInCurrentMonth];
    NSUInteger partialDaysCount = weeklyOrdinality - 1;
    
    NSDateComponents *components = [dayInThePreviousMonth YMDComponents];
    
    self.daysInPreviousMonth = [NSMutableArray arrayWithCapacity:partialDaysCount];
    for (int i = daysCount - partialDaysCount + 1; i < daysCount + 1; ++i) {
        WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];
        [self.daysInPreviousMonth addObject:calendarDay];
        [self.calendarDays addObject:calendarDay];
    }
}

- (void)calculateDaysInCurrentMonthWithDate:(NSDate *)date
{
    NSUInteger daysCount = [date numberOfDaysInCurrentMonth];
    NSDateComponents *components = [date YMDComponents];
    
    self.daysInCurrentMonth = [NSMutableArray arrayWithCapacity:daysCount];
    for (int i = 1; i < daysCount + 1; ++i) {
        WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];
        [self.daysInCurrentMonth addObject:calendarDay];
        [self.calendarDays addObject:calendarDay];
    }
}

- (void)calculateDaysInFollowingMonthWithDate:(NSDate *)date
{
    NSUInteger weeklyOrdinality = [[date lastDayOfCurrentMonth] weeklyOrdinality];
    if (weeklyOrdinality == 7) return ;
    
    NSUInteger partialDaysCount = 7 - weeklyOrdinality;
    NSDateComponents *components = [[date dayInTheFollowingMonth] YMDComponents];
    
    self.daysInFollowingMonth = [NSMutableArray arrayWithCapacity:partialDaysCount];
    for (int i = 1; i < partialDaysCount + 1; ++i) {
        WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];
        [self.daysInFollowingMonth addObject:calendarDay];
        [self.calendarDays addObject:calendarDay];
    }
}

到此,就可以顺利地展现出给定月份的日历控件了。最近项目比较忙,随手记一篇 :)

相关文章推荐

iOS之自定义简单的日历控件

iOS并没有系统日历控件,但是说白了就是基于UICollectionView的封装,对于UICollectionView我就不说他的创建了,主要难在数据的处理,下面我就说一下处理数据的思路: 我将数...

iOS 【一行代码集成一款精致的第三方OC日历——WZYCalendar】

一行代码集成一款精致的第三方OC日历——WZYCalendar

iOS控件之日历

iOS没用日历控件,所以我们需要自己进行封装,博主是用的CollectionView封装的 其实难就难在如何处理数据,下面上代码 这里是我封装的CollectionView,继承UICollectio...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

在iOS上实现一个简单的日历控件

先上图: 从图中可以看出日历控件就是由一个个小方块组成的,每一行有7个小方块,分别表示一周的星期天到星期六。 给定一个月份,我们首先需要知道这个月有多少周。那么如何确定一个月有...

UIDatePicker 日期/时间选取器(滚轮)—IOS开发

声明 欢迎转载,但是请尊重作者劳动成果,转载请保留此框内声明,谢谢。 文章出处:http://blog.csdn.net/iukey UIDatePi...
  • iukey
  • iukey
  • 2012年03月07日 00:58
  • 88800

iOS日历视图 FSCalendar的使用

由于在项目中需要使用到日历视图,无意中发现了一款开源的日历控件FSCalendar,在此介绍一下FSCalendar的使用. 一、安装 1.使用CocoaPods安装 在pod file中添加如...

IOS上的个人实现日历控件

#import #define But_TextFontColor [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0] #define B...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:在iOS上实现一个简单的日历控件
举报原因:
原因补充:

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