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

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

先上图:

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

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

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

  1. @interface NSDate (WQCalendarLogic)

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

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

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

  1. - (NSDate *)firstDayOfCurrentMonth
  2. {
  3. NSDate *startDate = nil;
  4. BOOL ok = [[NSCalendar currentCalendar] rangeOfUnit:NSMonthCalendarUnit startDate:&startDate interval:NULL forDate:self];
  5. NSAssert1(ok, @"Failed to calculate the first day of the month based on %@", self);
  6. return startDate;
  7. }
  8. - (NSUInteger)weeklyOrdinality
  9. {
  10. return [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:self];
  11. }

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

  1. - (NSUInteger)numberOfWeeksInCurrentMonth
  2. {
  3. NSUInteger weekday = [[self firstDayOfCurrentMonth] weeklyOrdinality];
  4. NSUInteger days = [self numberOfDaysInCurrentMonth];
  5. NSUInteger weeks = 0;
  6. if (weekday > 1) {
  7. weeks += 1, days -= (7 - weekday + 1);
  8. }
  9. weeks += days / 7;
  10. weeks += (days % 7 > 0) ? 1 : 0;
  11. return weeks;
  12. }

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

  1. @interface WQCalendarTileView : UIView

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

  1. @interface WQCalendarGridView : UIView
  2. @property (nonatomic, weak) id<WQCalendarGridViewDataSource> dataSource;
  3. @property (nonatomic, weak) id<WQCalendarGridViewDelegate> delegate;
  4. - (void)reloadData;

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

  1. @class WQCalendarGridView;
  2. @protocol WQCalendarGridViewDataSource <NSObject>
  3. @required
  4. - (NSUInteger)numberOfRowsInGridView:(WQCalendarGridView *)gridView;
  5. - (WQCalendarTileView *)gridView:(WQCalendarGridView *)gridView tileViewForRow:(NSUInteger)row column:(NSUInteger)column;
  6. @optional
  7. - (CGFloat)heightForRowInGridView:(WQCalendarGridView *)gridView;
  8. @end
  9. @protocol WQCalendarGridViewDelegate <NSObject>
  10. - (void)gridView:(WQCalendarGridView *)gridView didSelectAtRow:(NSUInteger)row column:(NSUInteger)column;
  11. @end

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

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

  1. #pragma mark - method to calculate days in previous, current and the following month.
  2. - (void)calculateDaysInPreviousMonthWithDate:(NSDate *)date
  3. {
  4. NSUInteger weeklyOrdinality = [[date firstDayOfCurrentMonth] weeklyOrdinality];
  5. NSDate *dayInThePreviousMonth = [date dayInThePreviousMonth];
  6. NSUInteger daysCount = [dayInThePreviousMonth numberOfDaysInCurrentMonth];
  7. NSUInteger partialDaysCount = weeklyOrdinality - 1;
  8. NSDateComponents *components = [dayInThePreviousMonth YMDComponents];
  9. self.daysInPreviousMonth = [NSMutableArray arrayWithCapacity:partialDaysCount];
  10. for (int i = daysCount - partialDaysCount + 1; i < daysCount + 1; ++i) {
  11. WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];
  12. [self.daysInPreviousMonth addObject:calendarDay];
  13. [self.calendarDays addObject:calendarDay];
  14. }
  15. }
  16. - (void)calculateDaysInCurrentMonthWithDate:(NSDate *)date
  17. {
  18. NSUInteger daysCount = [date numberOfDaysInCurrentMonth];
  19. NSDateComponents *components = [date YMDComponents];
  20. self.daysInCurrentMonth = [NSMutableArray arrayWithCapacity:daysCount];
  21. for (int i = 1; i < daysCount + 1; ++i) {
  22. WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];
  23. [self.daysInCurrentMonth addObject:calendarDay];
  24. [self.calendarDays addObject:calendarDay];
  25. }
  26. }
  27. - (void)calculateDaysInFollowingMonthWithDate:(NSDate *)date
  28. {
  29. NSUInteger weeklyOrdinality = [[date lastDayOfCurrentMonth] weeklyOrdinality];
  30. if (weeklyOrdinality == 7) return ;
  31. NSUInteger partialDaysCount = 7 - weeklyOrdinality;
  32. NSDateComponents *components = [[date dayInTheFollowingMonth] YMDComponents];
  33. self.daysInFollowingMonth = [NSMutableArray arrayWithCapacity:partialDaysCount];
  34. for (int i = 1; i < partialDaysCount + 1; ++i) {
  35. WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];
  36. [self.daysInFollowingMonth addObject:calendarDay];
  37. [self.calendarDays addObject:calendarDay];
  38. }
  39. }

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

copy from http://blog.csdn.net/jasonblog/article/details/21977481

iOS 日历控件的更多相关文章

  1. iOS日历控件

    项目需要,前一阵子重构了下iPad工程,添加了一个滚动无缝日历. 当时没有头绪,网上找了一个源码改吧改吧就上线了(参考链接),这个功能很多而且流畅性也特别好,推荐不会写的可以参考下. 这几天,活不太忙 ...

  2. iOS开发之自定义日历控件

    前言 日常开发中经常会遇到日期选择,为了方便使用,简单封装了一个日历控件,在此抛砖引玉供大家参考. 效果 功能 支持单选.区间 支持默认选中日期 支持限制月份 支持过去.当前.未来模式 支持frame ...

  3. IOS自定义日历控件的简单实现(附思想及过程)

    因为程序要求要插入一个日历控件,该空间的要求是从当天开始及以后的六个月内的日历,上网查资料基本上都说只要获取两个条件(当月第一天周几和本月一共有多少天)就可以实现一个简单的日历,剩下的靠自己的简单逻辑 ...

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

    http://blog.csdn.net/jasonblog/article/details/21977481 近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表. 不过再怎么复杂的控件, ...

  5. android日历控件(一)

    自定义日历并且具备设置今天以前的时间不可点选,以前的颜色和当前的颜色不同,以及获取两次点击日期之间间隔的天数所以说细节比较多 个人习惯,先上图 靠,笔记本不知道怎么回事,禁用到触摸板之后 再次唤醒屏幕 ...

  6. JS调用Android、Ios原生控件

    在上一篇博客中已经和大家聊了,关于JS与Android.Ios原生控件之间相互通信的详细代码实现,今天我们一起聊一下JS调用Android.Ios通信的相同点和不同点,以便帮助我们在进行混合式开发时, ...

  7. 初识IOS,Label控件的应用。

    初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...

  8. JQuery日历控件

    日历控件最后一弹——JQuery实现,换汤不换药.原理一模一样,换了种实现工具.关于日历的终于写完了,接下来研究研究nodejs.嗯,近期就这点事了. 同样还是将input的id设置成calendar ...

  9. 【转】【WebDriver】不可编辑域和日历控件域的输入 javascript

    http://blog.csdn.net/fudax/article/details/8089404 今天用到日历控件,用第一个javascript执行后页面上的日期控件后,在html中可以看到生效日 ...

随机推荐

  1. BZOJ 1927: [Sdoi2010]星际竞速(最小费用最大流)

    拆点,费用流... ----------------------------------------------------------------------------- #include< ...

  2. ThinkPHP第十三天(CONF_PATH、APP_PATH,UEditor用法)

    1.CONF_PATH 项目配置文件目录地址,APP_PATH 项目地址 2.ThinkPHP中更新数据的连接操作位save(),更新一个字段可以用setField(name,value)方法. 3. ...

  3. 简单的Coretext 图文混排

    在很多新闻类或有文字展示的应用中现在都会出现图文混排的界面例如网易新闻等,乍一看去相似一个网页,其实这样效果并非由UIWebView 加载网页实现.现在分享一种比较简单的实现方式 iOS sdk中为我 ...

  4. HDU题解索引

    HDU 1000 A + B Problem  I/O HDU 1001 Sum Problem  数学 HDU 1002 A + B Problem II  高精度加法 HDU 1003 Maxsu ...

  5. IOS_OC_id ,NSObject, id&lt;NSObject&gt;差别

    我们常常会混淆下面三种申明(我是没有留意过):     1. id foo1;     2. NSObject *foo2;     3. id<NSObject> foo3;     第 ...

  6. Android实现应用下载并自动安装apk包

    安装: ? 1 2 3 4 5 String str = "/CanavaCancel.apk"; String fileName = Environment.getExterna ...

  7. python下yield(生成器)

    python下的协程: #encoding=utf-8 """ 协程----微小的进程 yield生成器-----生成一个可迭代对象比如list, tuple,dir 1 ...

  8. 转换DataSet中的多个表为Excel中的多个Sheets

    第一种方法: 1. 在设计页面,有一个button按钮,当用户单击按钮的时候,发生转换 <asp:Button ID="Export" runat="server& ...

  9. dedecms的入门使用

    前段时间(其实也很远了)一直在学习dedecms,这里对前段时间的学习做一个总结. dedecms学习网址:http://help.dedecms.com/v53

  10. BZOJ 1642: [Usaco2007 Nov]Milking Time 挤奶时间( dp )

    水dp 先按开始时间排序 , 然后dp. dp( i ) 表示前 i 个时间段选第 i 个时间段的最优答案 , 则 dp( i ) = max( dp( j ) ) + w_i ( 0 < j ...