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

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

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

先上图:

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

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

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

  1. @interface NSDate (WQCalendarLogic)
@interface NSDate (WQCalendarLogic)

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

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

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. }
- (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,得到倍数和余数:

  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. }
- (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;
}

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

  1. @interface WQCalendarTileView : UIView
@interface WQCalendarTileView : UIView

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

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

@property (nonatomic, weak) id<WQCalendarGridViewDataSource> dataSource;
@property (nonatomic, weak) id<WQCalendarGridViewDelegate> delegate; - (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
@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进行配置,比如非当前月的以灰色展示,那么我们就需要知道当前月展示中包含的 上个月残留部分,和 下个月的开头部分:

  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. }
#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上实现一个简单的日历控件的更多相关文章

  1. 【VS开发】 自己编写一个简单的ActiveX控件——详尽教程

    最近开始学ActiveX控件编程,上手不太容易,上网想找相关教程也没合适的,最后还是在师哥的指导下完成了第一个简单控件的开发,现在把开发过程贴出来与大家分享一下~ (环境说明--平台:vs2005:语 ...

  2. 撸一个Android高性能日历控件,高仿魅族

    Android原生的CalendarView根本无法满足我们日常开发的需要,在开发吾记APP的过程中,我觉得需要来一款高性能且美观简洁的日历控件,觉得魅族的日历风格十分适合,于是打算撸一款. gith ...

  3. 分享一个WPF下日历控件(Calendar)的样式

    WPF日历控件的一个样式 WPF自带的日历控件样式可能会比较丑,要修改其样式看起来挺复杂的,实际上很简单,用Blend打开,修改三个模板,基本就能改变全部面貌,也很容易 先上图 样式如下: <S ...

  4. 调用ocx ActiveX控件详解(做一个简单的ocx控件)

    背景 最近做的项目都和插件有关,就是在页面中调用插件的方法,然后进行操作. 插件就是ocx ActiveX控件,具体的说明可以自己去了解一下,在这里就不做赘述. 具体调用方式很简单: 1.在页面中写一 ...

  5. Android史上功能最全的日历控件

    ※效果 ※用法 package com.fancyy.calendarweight; import java.util.ArrayList; import java.util.List; import ...

  6. 一个简单的Loading控件

    实现效果如下: 使用方法: 在layout文件中添加以下代码: <com.example.jack.ui.widget.RingLoading android:layout_width=&quo ...

  7. Android自定义控件之日历控件

      标签: android 控件 日历 应用 需求 2015年09月26日 22:21:54 25062人阅读 评论(109) 收藏 举报 分类: Android自定义控件系列(7) 版权声明:转载注 ...

  8. jquery插件——日历控件

    今天在网上有看到一个jquery插件——日历控件,不过之前也在柯乐义的网站上看到了(http://keleyi.com/ 推荐下) 这个插件看着比较大气,所以干脆也分享下,以后自己也好用一点儿 1.页 ...

  9. 怎样在Android实现桌面清理内存简单Widget小控件

    怎样在Android实现桌面清理内存简单Widget小控件 我们常常会看到类似于360.金山手机卫士一类的软件会带一个widget小控件,显示在桌面上,上面会显示现有内存大小,然后会带一个按键功能来一 ...

随机推荐

  1. 中国剩余定理算法详解 + POJ 1006 Biorhythms 生理周期

    转载请注明出处:http://exp-blog.com/2018/06/24/pid-1054/ #include <iostream> #include <cstdio> u ...

  2. TouTiao开源项目 分析笔记7 加载数据的过程

    1.以新闻页中的段子数据显示为例 1.1.首先执行InitApp==>SplashActivity. 因为在AndroidManifest.xml中定义了一个<intent-filter& ...

  3. 16,docker入门

      在学一门新知识的时候,超哥喜欢提问,why?what?how? wiki资料 什么是docker Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一 ...

  4. 《Cracking the Coding Interview》——第1章:数组和字符串——题目5

    2014-03-18 01:40 题目:对字符串进行类似游程编码的压缩,如果压缩完了长度更长,则返回不压缩的结果.比如:aabcccccaaa->a2b1c5a3,abc->abc. 解法 ...

  5. python学习笔记三:函数及变量作用域

    一.定义 def functionName([arg1,arg2,...]): code 二.示例 #!/usr/bin/python #coding:utf8 #coding=utf8 #encod ...

  6. awk学习笔记

    1. 数据格式 id1,n1 id2,n2 ... 要对每个id进行一个n的加和 cat file1 | awk -F"," '{if(n[$1]>0){n[$1]=n[$1 ...

  7. 使用java实现对称加密解密(AES),非对称加密解密(RSA)

    对称加密:双方采用同样的秘钥进行加密和解密.特点是速度快,但是安全性没有非对称加密高 非对称加密:接收方生成的公有秘钥公布给发送方,发送方使用该公有秘钥加密之后,发送给接收方,然后接收方使用私有秘钥解 ...

  8. 在Struts2 Action中快速简便的访问Request、Session等变量

    前言——正常情况下如何在Action中获取到这些变量 全部方法(共四种)可以参考:http://blog.csdn.net/itmyhome1990/article/details/7019476 这 ...

  9. js 图片自动循环切换setInterval();

    stlye样式定义 <style type="text/css">             body{background-image: url(img/001.jpg ...

  10. oracle存储过程粗解

    存储过程创建的语法: create or replace procedure 存储过程名(param1 in type,param2 out type) as 变量1 类型(值范围);变量2 类型(值 ...