首页 > 代码库 > 在iOS上实现一个简单的日历控件
在iOS上实现一个简单的日历控件
http://blog.csdn.net/jasonblog/article/details/21977481
近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表。
不过再怎么复杂的控件,也是由基础的零配件组装起来的,这里最基本的就是日历控件。
先上图:
从图中可以看出日历控件就是由一个个小方块组成的,每一行有7个小方块,分别表示一周的星期天到星期六。
给定一个月份,我们首先需要知道这个月有多少周。那么如何确定一个月有多少周呢?
我是这么想的,在NSDate上做扩展:
[cpp] view plaincopyprint?
- @interface NSDate (WQCalendarLogic)
@interface NSDate (WQCalendarLogic)
0. 首先需要知道这个月有多少天:
[cpp] view plaincopyprint?
- - (NSUInteger)numberOfDaysInCurrentMonth
- {
- // 频繁调用 [NSCalendar currentCalendar] 可能存在性能问题
- return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;
- }
- (NSUInteger)numberOfDaysInCurrentMonth{ // 频繁调用 [NSCalendar currentCalendar] 可能存在性能问题 return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;}
1. 确定这个月的第一天是星期几。这样就能知道给定月份的第一周有几天:
[cpp] view plaincopyprint?
- - (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];
- }
- (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,得到倍数和余数:
[cpp] view plaincopyprint?
- - (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;
- }
- (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;}
到这里,就可以知道一个月有多少行多少列,从而计算出需要多少个小方块来展示:
[cpp] view plaincopyprint?
- @interface WQCalendarTileView : UIView
@interface WQCalendarTileView : UIView
这些小方块用一个大方块来承载:
[cpp] view plaincopyprint?
- @interface WQCalendarGridView : UIView
- @property (nonatomic, weak) id<WQCalendarGridViewDataSource> dataSource;
- @property (nonatomic, weak) id<WQCalendarGridViewDelegate> delegate;
- - (void)reloadData;
@interface WQCalendarGridView : UIView@property (nonatomic, weak) id<WQCalendarGridViewDataSource> dataSource;@property (nonatomic, weak) id<WQCalendarGridViewDelegate> delegate;- (void)reloadData;
和UITableView类似,当WQCalendarGridView调用reloadData接口时,会开始进行布局。而布局所需要的信息由dataSource和delegate提供:
[cpp] view plaincopyprint?
- @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
@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进行配置,比如非当前月的以灰色展示,那么我们就需要知道当前月展示中包含的 上个月残留部分,和 下个月的开头部分:
[cpp] view plaincopyprint?
- #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];
- }
- }
#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上实现一个简单的日历控件
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。