在iOS上实现一个简单的日历控件
http://blog.csdn.net/jasonblog/article/details/21977481
近期需要写一个交互有点DT的日历控件,具体交互细节这里略过不表。
不过再怎么复杂的控件,也是由基础的零配件组装起来的,这里最基本的就是日历控件。
先上图:
从图中可以看出日历控件就是由一个个小方块组成的,每一行有7个小方块,分别表示一周的星期天到星期六。
给定一个月份,我们首先需要知道这个月有多少周。那么如何确定一个月有多少周呢?
我是这么想的,在NSDate上做扩展:
- @interface NSDate (WQCalendarLogic)
@interface NSDate (WQCalendarLogic)
0. 首先需要知道这个月有多少天:
- - (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. 确定这个月的第一天是星期几。这样就能知道给定月份的第一周有几天:
- - (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,得到倍数和余数:
- - (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;
}
到这里,就可以知道一个月有多少行多少列,从而计算出需要多少个小方块来展示:
- @interface WQCalendarTileView : UIView
@interface WQCalendarTileView : UIView
这些小方块用一个大方块来承载:
- @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提供:
- @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进行配置,比如非当前月的以灰色展示,那么我们就需要知道当前月展示中包含的 上个月残留部分,和 下个月的开头部分:
- #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上实现一个简单的日历控件的更多相关文章
- 【VS开发】 自己编写一个简单的ActiveX控件——详尽教程
最近开始学ActiveX控件编程,上手不太容易,上网想找相关教程也没合适的,最后还是在师哥的指导下完成了第一个简单控件的开发,现在把开发过程贴出来与大家分享一下~ (环境说明--平台:vs2005:语 ...
- 撸一个Android高性能日历控件,高仿魅族
Android原生的CalendarView根本无法满足我们日常开发的需要,在开发吾记APP的过程中,我觉得需要来一款高性能且美观简洁的日历控件,觉得魅族的日历风格十分适合,于是打算撸一款. gith ...
- 分享一个WPF下日历控件(Calendar)的样式
WPF日历控件的一个样式 WPF自带的日历控件样式可能会比较丑,要修改其样式看起来挺复杂的,实际上很简单,用Blend打开,修改三个模板,基本就能改变全部面貌,也很容易 先上图 样式如下: <S ...
- 调用ocx ActiveX控件详解(做一个简单的ocx控件)
背景 最近做的项目都和插件有关,就是在页面中调用插件的方法,然后进行操作. 插件就是ocx ActiveX控件,具体的说明可以自己去了解一下,在这里就不做赘述. 具体调用方式很简单: 1.在页面中写一 ...
- Android史上功能最全的日历控件
※效果 ※用法 package com.fancyy.calendarweight; import java.util.ArrayList; import java.util.List; import ...
- 一个简单的Loading控件
实现效果如下: 使用方法: 在layout文件中添加以下代码: <com.example.jack.ui.widget.RingLoading android:layout_width=&quo ...
- Android自定义控件之日历控件
标签: android 控件 日历 应用 需求 2015年09月26日 22:21:54 25062人阅读 评论(109) 收藏 举报 分类: Android自定义控件系列(7) 版权声明:转载注 ...
- jquery插件——日历控件
今天在网上有看到一个jquery插件——日历控件,不过之前也在柯乐义的网站上看到了(http://keleyi.com/ 推荐下) 这个插件看着比较大气,所以干脆也分享下,以后自己也好用一点儿 1.页 ...
- 怎样在Android实现桌面清理内存简单Widget小控件
怎样在Android实现桌面清理内存简单Widget小控件 我们常常会看到类似于360.金山手机卫士一类的软件会带一个widget小控件,显示在桌面上,上面会显示现有内存大小,然后会带一个按键功能来一 ...
随机推荐
- Nginx技术深入剖析
Nginx软件功能模块说明 核心功能模块(Core functionality):主要对应配置文件的Main区块和Events区块. 标准的http功能模块: 企业 场景常用的Nginx http功能 ...
- PHP代码审计4-漏洞挖掘思路
漏洞挖掘思路 漏洞形成的条件 1.变量可控制 2.变量可到达有利用价值的函数(危险函数) 漏洞造成的效果 漏洞的利用效果取决于最终的函数功能,变量进入什么样的函数就导致什么样的效果 危险函数 文件包含 ...
- 10.2 ajax
Ajax Ajax简介 AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”.即使用Javascript语言与服务器进行异步交互 ...
- 2,Flask 中的 Render Redirect HttpResponse
一,Flask中的HTTPResponse 在Flask 中的HttpResponse 在我们看来其实就是直接返回字符串 二,.Flask中的Redirect 每当访问"/redi" ...
- Hihocoder 1275 扫地机器人 计算几何
题意: 有一个房间的形状是多边形,而且每条边都平行于坐标轴,按顺时针给出多边形的顶点坐标 还有一个正方形的扫地机器人,机器人只可以上下左右移动,不可以旋转 问机器人移动的区域能不能覆盖整个房间 分析: ...
- 安测云验证有CTA问题
背景: 现在所有的app 都需要通过工信部的审核.用户不同意之前,不能联网. 那么,我怎么知道自己的应用有没有联网呢?那么多sdk ,那么多代码?我怎么测试呢? 哈哈,我们测试给的方法真的很管用. l ...
- sublimeText3快捷键
Ctrl+Shift+P:打开命令面板Ctrl+P:搜索项目中的文件Ctrl+G:跳转到第几行Ctrl+W:关闭当前打开文件Ctrl+Shift+W:关闭所有打开文件Ctrl+Shift+V:粘贴 ...
- 通过slf4j/log4j的MDC/NDC 实现日志追踪
在分布式系统或者较为复杂的系统中,我们希望可以看到一个客户请求的处理过程所涉及到的所有子系统\模块的处理日志. 由于slf4j/log4j基本是日志记录的标准组件,所以slf4j/log4j成为了我的 ...
- python学习笔记-基础
1.大小写敏感 2. print (n,f,s1,s2,s3,s4,sep='\n') -- 换行输出 seq='\n' print ('n=%d'%n,'f=%f'%f,'s1=%s'%s1,' ...
- sublime3 Package Control和 中文安装
sublime3中文版需要使用PackageControl,所以首先需要安装PackageControl 一.PackageControl安装: 1.点击Preferences > Browse ...