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

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

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

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

先上图:

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

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

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

[cpp] view plain copy

  1. @interface NSDate (WQCalendarLogic)

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

[cpp] view plain copy

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

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

[cpp] view plain copy

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

[cpp] view plain copy

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

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

[cpp] view plain copy

  1. @interface WQCalendarTileView : UIView

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

[cpp] view plain copy

  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提供:

[cpp] view plain copy

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

[cpp] view plain copy

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

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

时间: 2024-10-07 05:30:37

在iOS上实现一个简单的日历控件的相关文章

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

Android原生的CalendarView根本无法满足我们日常开发的需要,在开发吾记APP的过程中,我觉得需要来一款高性能且美观简洁的日历控件,觉得魅族的日历风格十分适合,于是打算撸一款. github地址:https://github.com/huanghaibin-dev/CalendarView compile 'com.haibin:calendarview:1.0.2' 先上效果图: 动手之前我们需要分析一下魅族是怎么设计如此高性能的日历的,我们打开开发者选项中的显示布局边界: 好吧

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

※效果 ※用法 package com.fancyy.calendarweight; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.view.

一个简单的Loading控件

实现效果如下: 使用方法: 在layout文件中添加以下代码: <com.example.jack.ui.widget.RingLoading android:layout_width="20.0dip" android:layout_height="20.0dip" /> 具体代码如下: package com.example.jack.ui.widget; import android.content.Context; import android.

Android自定义控件之日历控件

Android自定义控件之日历控件 2015-10-23 Android开发中文站 三月份学习android,至今也有半年有余,中间也做过两个项目,但是依然感觉自己做的应用不是很有新意,比不上应用市场上那些应用如此绚丽.所以自己仍需继续努力.学习至今,仍感觉自定义控件是一块硬骨头,还没修炼到身后的内功,下面就切入正题,以一次项目的需求,来实现一个自定义的日历控件.效果图先来一发. 我们分析下效果图,然后确定我们的需求. (1).绘制星期的自定义View,用于标识日期的礼拜. (2).绘制日期的自

iOS中UITableViewController自带的刷新控件

iOS中UITableViewController自带的刷新控件 一.引言 在iOS开发中,使用tableView的界面,大多会用到一个下拉刷新的的控件,第三方库中,我们一般会选择比较好用的MJRefresh,其实,在iOS6之后,系统为我们提供了一个原生的刷新控件,使用起来非常方便,只是制定性不强,如果我们没有复杂的需求,使用UIRefreshControl也是不错的一个选择. 二.UITableViewController 相对于UIViewController,UITableViewCon

如何在Android实现桌面清理内存简单Widget小控件

如何在Android实现桌面清理内存简单Widget小控件 我们经常会看到类似于360.金山手机卫士一类的软件会带一个widget小控件,显示在桌面上,上面会显示现有内存大小,然后会带一个按键功能来一键清理内存,杀死后台进程的功能,那么这个功能是如何实现的呢,我们今天也来尝试做一个类似的功能的小控件. 效果图: 一.UI部分的编写: 参照Google的文档,首先在建立一个类继承AppWidgetProvider import android.appwidget.AppWidgetProvider

javaScript实现日历控件

最近学习js,闲来无事就写了个简单的日历控件,不知道别人是怎么实现的,纯粹自己想法写的, 写的不好 ,但可以起到抛砖引玉的作用. 先来看效果. 调用时候的代码 就一句,引入我写的js文件, 前提text框必须有rlk="rl"  才会被我识别成要做日历控件的文本框, 我回把它设为readonly js代码也很简单,一些简单的算法,然后画出div 里面再画个table,根据文本框的位置进行定位就行了..代码很简单 大家可以看看. 我把完整代码和实例放在一起,大家可以去免费下载. 下载地址

日历控件源码

前两天需要个简单的日历控件,网上搜了一下居然还有人卖,估计是提供其他维护服务吧,反正我是用不到的,自己手写了个凑合用,自己写的兼容性没测过,如果有什么问题大家给点改进意见. 先上图 源码 <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <script type="text

iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 - 文顶顶

原文  http://www.cnblogs.com/wendingding/p/3771047.html iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 一.实现效果 说明: 点击随机按钮,能够自动选取,下方数据自动刷新. 二.实现思路 1.picker view的有默认高度为162,不可修改. 2.显示数据,需要设置数据源,也有两种方式(成为数据源,遵守协议) 3.实现数据源里面的两个方法 1)返回一共有多少列 2)在这一列中一共有多少行 4.通过代理告诉它那一列的哪