在iOS上实现一个简单的日历控件
2016-05-25 00:00
639 查看
摘要: 在iOS上实现一个简单的日历控件
近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表。
不过再怎么复杂的控件,也是由基础的零配件组装起来的,这里最基本的就是日历控件。
先上图:
从图中可以看出日历控件就是由一个个小方块组成的,每一行有7个小方块,分别表示一周的星期天到星期六。
给定一个月份,我们首先需要知道这个月有多少周。那么如何确定一个月有多少周呢?
我是这么想的,在NSDate上做扩展:
[cpp] view plain copy
@interface NSDate (WQCalendarLogic)
0. 首先需要知道这个月有多少天:
[cpp] view plain copy
- (NSUInteger)numberOfDaysInCurrentMonth
{
// 频繁调用 [NSCalendar currentCalendar] 可能存在性能问题
return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;
}
1. 确定这个月的第一天是星期几。这样就能知道给定月份的第一周有几天:
[cpp] view plain copy
- (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 plain copy
- (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 plain copy
@interface WQCalendarTileView : UIView
这些小方块用一个大方块来承载:
[cpp] view plain copy
@interface WQCalendarGridView : UIView
@property (nonatomic, weak) id<WQCalendarGridViewDataSource> dataSource;
@property (nonatomic, weak) id<WQCalendarGridViewDelegate> delegate;
- (void)reloadData;
和UITableView类似,当WQCalendarGridView调用reloadData接口时,会开始进行布局。而布局所需要的信息由dataSource和delegate提供:
[cpp] view plain copy
@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 plain copy
#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];
}
}
到
近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表。
不过再怎么复杂的控件,也是由基础的零配件组装起来的,这里最基本的就是日历控件。
先上图:
从图中可以看出日历控件就是由一个个小方块组成的,每一行有7个小方块,分别表示一周的星期天到星期六。
给定一个月份,我们首先需要知道这个月有多少周。那么如何确定一个月有多少周呢?
我是这么想的,在NSDate上做扩展:
[cpp] view plain copy
@interface NSDate (WQCalendarLogic)
0. 首先需要知道这个月有多少天:
[cpp] view plain copy
- (NSUInteger)numberOfDaysInCurrentMonth
{
// 频繁调用 [NSCalendar currentCalendar] 可能存在性能问题
return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;
}
1. 确定这个月的第一天是星期几。这样就能知道给定月份的第一周有几天:
[cpp] view plain copy
- (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 plain copy
- (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 plain copy
@interface WQCalendarTileView : UIView
这些小方块用一个大方块来承载:
[cpp] view plain copy
@interface WQCalendarGridView : UIView
@property (nonatomic, weak) id<WQCalendarGridViewDataSource> dataSource;
@property (nonatomic, weak) id<WQCalendarGridViewDelegate> delegate;
- (void)reloadData;
和UITableView类似,当WQCalendarGridView调用reloadData接口时,会开始进行布局。而布局所需要的信息由dataSource和delegate提供:
[cpp] view plain copy
@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 plain copy
#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开发中的错误整理,再一次整理通过通知中心来处理键盘,一定记得最后关闭通知中心
- IOS数据存储简述
- IOS之路--OC之继承
- CocoaPods 如何正确的上传到SVN?
- iOS开发小技巧--根据文字,计算label中文字高度
- 关于iOS的强引用,弱引用及strong,retain,copy,weak,assignd的关系
- iOS TableView上面有空白的区域时的处理
- iOS开发:NSString用法大全
- iOS中基于BLE的学习博客
- IOS8 开发指南报错Thread 1:signal SIGABRT
- 基于SlipButton教你如何实现ios7的选择器效果
- iOS开发实现剪贴板操作复制粘贴功能
- iOS开发代码规范
- iOS开发_iPhone 实现剪贴板操作_iPhone 复制粘贴功能
- iOS中创建单例的两种方式
- iOS屏幕强制旋转
- iOS开发中,实现两个控制器之间数据的双向传递
- -ObjC, -all_load, -force_load
- iOS BSD Socket UDP编程