IOS自定义日历控件的简单实现(附思想及过程)
因为程序要求要插入一个日历控件,该空间的要求是从当天开始及以后的六个月内的日历,上网查资料基本上都说只要获取两个条件(当月第一天周几和本月一共有多少天)就可以实现一个简单的日历,剩下的靠自己的简单逻辑就OK了,下面开始自己从开始到完成的整个过程
1,首先做NSDate类目,扩展一些方法让日期之间转换更加方便
#import <Foundation/Foundation.h> @interface NSDate (LYWCalendar) #pragma mark - 获取日
- (NSInteger)day:(NSDate *)date;
#pragma mark - 获取月
- (NSInteger)month:(NSDate *)date;
#pragma mark - 获取年
- (NSInteger)year:(NSDate *)date;
#pragma mark - 获取当月第一天周几
- (NSInteger)firstWeekdayInThisMonth:(NSDate *)date;
#pragma mark - 获取当前月有多少天
- (NSInteger)totaldaysInMonth:(NSDate *)date; @end
下面一一实现这些方法
#import "NSDate+LYWCalendar.h" @implementation NSDate (LYWCalendar) /**
*实现部分
*/
#pragma mark -- 获取日
- (NSInteger)day:(NSDate *)date{
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
return components.day;
} #pragma mark -- 获取月
- (NSInteger)month:(NSDate *)date{
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
return components.month;
} #pragma mark -- 获取年
- (NSInteger)year:(NSDate *)date{
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
return components.year;
} #pragma mark -- 获得当前月份第一天星期几
- (NSInteger)firstWeekdayInThisMonth:(NSDate *)date{
NSCalendar *calendar = [NSCalendar currentCalendar];
//设置每周的第一天从周几开始,默认为1,从周日开始
[calendar setFirstWeekday:];//1.Sun. 2.Mon. 3.Thes. 4.Wed. 5.Thur. 6.Fri. 7.Sat.
NSDateComponents *comp = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
[comp setDay:];
NSDate *firstDayOfMonthDate = [calendar dateFromComponents:comp];
NSUInteger firstWeekday = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:firstDayOfMonthDate];
//若设置从周日开始算起则需要减一,若从周一开始算起则不需要减
return firstWeekday - ;
}
#pragma mark -- 获取当前月共有多少天 - (NSInteger)totaldaysInMonth:(NSDate *)date{
NSRange daysInLastMonth = [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
return daysInLastMonth.length;
}
接下来就要写逻辑部分了,在ViewController里面实现,先说思想,首先想到的是用collectionView,但是每个月天数不一样在日历中的显示就不一样,有时候有五行有时候有六行,既然要用自动填充就必须考虑到这一点,下面是代码实现
#import "ViewController.h"
#import "Macro.h"
#import "NSDate+LYWCalendar.h"
#import "LYWCollectionViewCell.h"
#import "LYWCollectionReusableView.h"
定义一些全局变量
static NSString *cellID = @"cellID";
static NSString *headerID = @"headerID";
static NSString *footerID = @"footerID"; @implementation ViewController
{
//自动布局
UICollectionViewFlowLayout *_layout;
//表格视图
UICollectionView *_collectionView;
//当月第一天星期几
NSInteger firstDayInMounthInWeekly;
NSMutableArray *_firstMounth;
//容纳六个数组的数组
NSMutableArray *_sixArray; }
//定义星期视图,若为周末则字体颜色为绿色
self.automaticallyAdjustsScrollViewInsets = NO;//关闭自动适应
NSArray *weekTitleArray = @[@"周日",@"周一",@"周二",@"周三",@"周四",@"周五",@"周六"];
for (int i = ; i < weekTitleArray.count; i++) {
UILabel *weekTitleLable = [[UILabel alloc]initWithFrame:CGRectMake(i * ((ScreenWidth/(weekTitleArray.count))), , ScreenWidth/(weekTitleArray.count ), )];
if (i == || i == ) {
weekTitleLable.textColor = [UIColor greenColor];
}else{
weekTitleLable.textColor = [UIColor blackColor];
}
weekTitleLable.text = [weekTitleArray objectAtIndex:i];
weekTitleLable.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:weekTitleLable];
}
//设置collectionView及自动布局,代理方法尤为重要
_layout = [[UICollectionViewFlowLayout alloc]init];
//头部始终在顶端
_layout.sectionHeadersPinToVisibleBounds = YES;
//头部视图高度
_layout.headerReferenceSize = CGSizeMake(, );
_layout.minimumLineSpacing = ;
_layout.minimumInteritemSpacing = ;
_collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(, + , ScreenWidth, ScreenHeight - - ) collectionViewLayout:_layout];
_collectionView.backgroundColor = [UIColor whiteColor];
//注册表格
[_collectionView registerClass:[LYWCollectionViewCell class] forCellWithReuseIdentifier:cellID];
//注册头视图
[_collectionView registerClass:[LYWCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerID];
//注册尾视图
// [_collectionView registerClass:[UICollectionReusableView class] forCellWithReuseIdentifier:footerID];
_collectionView.delegate = self;
_collectionView.dataSource = self;
[self.view addSubview:_collectionView];
逻辑部分,这里有个比较长的三项表达式
(daysInMounth > 29 && (firstDayInThisMounth == 6 || firstDayInThisMounth ==5) ? 42 : 35)
就是日历到底是六行还是七行,这就要根据日历的特性来判断了,如果当月天数大于29天并且当月第一天星期六(以这个程序的准则)或者星期天是返回六行剩下的返回三行,也有可能返回四行的,但是就这个程序来说是不可能的也就不需要做判断了
//NumberMounthes 为宏定义,表示要显示月的个数,程序要求是六个月,所以宏定义为六
//#define NumberMounthes 6 //想要展示的月数
//创建六个数组,并将这六个数组装入大数组中
_sixArray = [[NSMutableArray alloc]init];
for (int i = ; i < NumberMounthes ; i++ ) {
NSMutableArray *array = [[NSMutableArray alloc]init];
[_sixArray addObject:array];
}
//为六个数组写入每个月的日历信息
for (int i = ; i < NumberMounthes; i++) {
//获取月份
int mounth = ((int)[currentDate month:currentDate] + i)%;
NSDateComponents *components = [[NSDateComponents alloc]init];
//获取下个月的年月日信息,并将其转为date
components.month = mounth;
components.year = + mounth/;
components.day = ;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *nextDate = [calendar dateFromComponents:components];
//获取该月第一天星期几
NSInteger firstDayInThisMounth = [nextDate firstWeekdayInThisMonth:nextDate];
//该月的有多少天daysInThisMounth
NSInteger daysInThisMounth = [nextDate totaldaysInMonth:nextDate];
NSString *string = [[NSString alloc]init];
for (int j = ; j < (daysInMounth > && (firstDayInThisMounth == || firstDayInThisMounth ==) ? : ) ; j++) {
if (j < firstDayInThisMounth || j > daysInThisMounth + firstDayInThisMounth - ) {
string = @"";
[[_sixArray objectAtIndex:i]addObject:string];
}else{
string = [NSString stringWithFormat:@"%ld",j - firstDayInThisMounth + ];
[[_sixArray objectAtIndex:i]addObject:string];
}
}
}
下面是代理方法
//这两个不用说,返回cell个数及section个数
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [[_sixArray objectAtIndex:section] count];
} - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return _sixArray.count;
}
//这里是自定义cell,非常简单的自定义
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
LYWCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
UIView *blackgroundView = [[UIView alloc]initWithFrame:CGRectMake(, , cell.frame.size.width, cell.frame.size.height)];
blackgroundView.backgroundColor = [UIColor yellowColor];
cell.dateLable.text = [[_sixArray objectAtIndex:indexPath.section]objectAtIndex:indexPath.row];
NSDate *date = [[NSDate alloc]init];
NSInteger day = [date day:date];
//设置单击后的颜色
cell.selectedBackgroundView = blackgroundView;
return cell;
}
自定义cell .h
#import <UIKit/UIKit.h> @interface LYWCollectionViewCell : UICollectionViewCell @property (nonatomic,strong) UILabel *dateLable; - (instancetype)initWithFrame:(CGRect)frame; @end
.m
#import "LYWCollectionViewCell.h"
@implementation LYWCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame{
if (self == [super initWithFrame:frame]) {
_dateLable = [[UILabel alloc] initWithFrame:self.bounds];
[_dateLable setTextAlignment:NSTextAlignmentCenter];
[_dateLable setFont:[UIFont systemFontOfSize:]];
_dateLable.textColor = [UIColor blackColor];
[self addSubview:_dateLable];
}
return self;
}
@end
接着代理
//cell大小及间距
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(ScreenWidth/, ScreenWidth/);
} - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
return UIEdgeInsetsMake(, , , );
}
既然有日历,总得显示哪年哪月吧,前面已经注册表头视图了,这里只需要实现以下代理方法即可
collectionView有点不同其头视图也有单独的类,和cell一样先自定义headCell,也是非常简单的自定义
.h文件
#import <UIKit/UIKit.h> @interface LYWCollectionReusableView : UICollectionReusableView @property (nonatomic,strong) UILabel *dateLable; - (instancetype)initWithFrame:(CGRect)frame; @end
.m文件
#import "LYWCollectionReusableView.h"
@implementation LYWCollectionReusableView
- (instancetype)initWithFrame:(CGRect)frame{
if (self == [super initWithFrame:frame]) {
_dateLable = [[UILabel alloc] initWithFrame:self.bounds];
[_dateLable setTextAlignment:NSTextAlignmentLeft];
[_dateLable setFont:[UIFont systemFontOfSize:]];
_dateLable.textColor = [UIColor blackColor];
[self addSubview:_dateLable];
}
return self;
}
@end
接着代理方法,这里也有个三项判断式,和上面的大同小异,主要是防止12月显示为0月
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
if (kind == UICollectionElementKindSectionHeader) {
LYWCollectionReusableView *headerRV = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:headerID forIndexPath:indexPath];
//自定义蓝色
headerRV.backgroundColor = DODGER_BLUE;
NSDate *currentDate = [[NSDate alloc]init];
NSInteger year = ([currentDate month:currentDate] + indexPath.section)/ + ;
NSInteger mounth = ([currentDate month:currentDate] + indexPath.section) % == ? : ([currentDate month:currentDate] + indexPath.section)%;
headerRV.dateLable.text = [NSString stringWithFormat:@"%ld年%ld月",year,mounth];
return headerRV;
}else{
return nil;
}
}
还是代理,处理选中效果,选中的为黄色
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
LYWCollectionViewCell *cell = [self collectionView:_collectionView cellForItemAtIndexPath:indexPath];
NSDate *currentDate = [[NSDate alloc]init];
//打印当前日期
if (![cell.dateLable.text isEqualToString:@""]) {
NSInteger year = ([currentDate month:currentDate] + indexPath.section)/12 + 2016;
NSInteger mounth = ([currentDate month:currentDate] + indexPath.section)%12;
NSInteger day = [cell.dateLable.text intValue];
NSLog(@"%ld年%02ld月%02ld日",year,mounth,day);
}
//排除空值cell
//获取月份
NSInteger mounth = ([currentDate month:currentDate] + indexPath.section) % 12 == 0 ? 12 : ([currentDate month:currentDate] + indexPath.section)%12;
NSDateComponents *components = [[NSDateComponents alloc]init];
components.month = mounth;
components.year = 2016 + mounth/12;
components.day = 1;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *nextDate = [calendar dateFromComponents:components];
//获取该月第一天星期几
NSInteger firstDayInThisMounth = [nextDate firstWeekdayInThisMonth:nextDate];
//该月的有多少天daysInThisMounth
NSInteger daysInThisMounth = [nextDate totaldaysInMonth:nextDate];
if ((indexPath.row < firstDayInThisMounth || indexPath.row > daysInThisMounth + firstDayInThisMounth - 1)){
//如果点击空表格则单击无效
[collectionView cellForItemAtIndexPath:indexPath].userInteractionEnabled = NO;
[collectionView reloadData];
}
}
最后展示很烂的效果图

IOS自定义日历控件的简单实现(附思想及过程)的更多相关文章
- javascript实例学习之六—自定义日历控件
基于之前上篇博客轻量级jquery,tool.js和base.js.自定义开发的base_datePicker插件,效果类似于jquery_ui的datePicker插件 //基于Base.js以及t ...
- Android自定义日历控件(继承系统控件实现)
Android自定义日历控件(继承系统控件实现) 主要步骤 编写布局 继承LinearLayout设置子控件 设置数据 继承TextView实现有圆圈背景的TextView 添加Attribute 添 ...
- iOS开发之自定义日历控件
前言 日常开发中经常会遇到日期选择,为了方便使用,简单封装了一个日历控件,在此抛砖引玉供大家参考. 效果 功能 支持单选.区间 支持默认选中日期 支持限制月份 支持过去.当前.未来模式 支持frame ...
- android 自定义日历控件
日历控件View: /** * 日历控件 功能:获得点选的日期区间 * */ public class CalendarView extends View implements View.OnTouc ...
- arcgis api 3.x for js 共享干货系列之二自定义 Navigation 控件样式风格(附源码下载)
0.内容概览 自定义 Navigation 控件样式风格 源码下载 1.内容讲解 arcgis api 3.x for js 默认的Navigation控件样式风格如下图:这样的风格不能说不好,各有各 ...
- iOS 搜索框控件 最简单的dome
刚学习搜索框控件,写了个最简单的dome #import <UIKit/UIKit.h> .h @interface ViewController : UIViewController&l ...
- Android 一个日历控件的实现代码
转载 2017-05-19 作者:Othershe 我要评论 本篇文章主要介绍了Android 一个日历控件的实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看 ...
- ExtJs5_继承自定义一个控件
Extjs的开发都可以遵循OOP的原则,其对类的封装也很完善了.自定义一个控件最简单的办法就是继承一个已有的控件.根据上一节的需要,我做了一个Button的子类.首先根据目录结构,在app目录下建立一 ...
- 6、手把手教你Extjs5(六)继承自定义一个控件
Extjs的开发都可以遵循OOP的原则,其对类的封装也很完善了.自定义一个控件最简单的办法就是继承一个已有的控件.根据上一节的需要,我做了一个Button的子类.首先根据目录结构,在app目录下建立一 ...
随机推荐
- Ajax聊天
结构: index.html <!DOCTYPE html> <html> <head> <title>index.html</title> ...
- C语言课程学习的总结
C语言课程学习的总结 学习C程序这门课一年了,这是我们学的第一门专业课.在大学里,C语言不但是计算机专业的必修课程而且也是非计算机专业学习计算机基础的一门必修课程.所以作为我这个计算机专业的学生来说当 ...
- Ubuntu中由root用户修改为普通用户的办法
比如你的普通用户名是test 目前是root用户 键入命令 su - test 就可以了
- Unity 3D 正交相机(Orthographic)
1. Camera.aspect 表示摄像机显示区域的纵横比.宽高比,摄像机初始化的时候会默认设置成当前屏幕的宽高比,可以更改,也可以通过 Camera.ResetAspect 来重置. 2. Cam ...
- jQuery对象与dom对象的转换
一直以来对于通过jQuery方式获取的对象使不能直接使用JavaScript的一些方法的,开始的时候不理解,现在此案知道,原来jQuery获得的对象并不和我们平时使用getElementById获得的 ...
- Win服务器常用批处理脚本
oracle数据库备份 先导出数据库,然后执行压缩,将源文件删除,保留压缩文件 exp crm/crm@orcl file=G:\数据库备份\CRM\CRM%DATE%.dmp owner=crm&q ...
- vs2015打包winform程序遇到的一系列问题
1.因为打包的时候用的是release版本的东西,所以就先把项目按release编译一下,然后一大波bug,后来修改了生成目标平台为x86,我的解决方案里面加上安装部署项目共5个(ui配置:活动rel ...
- enable feature AJAX of MOSS2007
As default, the feature AJAX of MOSS2007 is disabled, so the site web configuration file should be m ...
- C#基础总结
1.执行.NET应用程序时经历的几个步骤 用C#编写应用程序代码 把应用程序代码编译为中间语言代码(MSIL),存储在程序集中 使用JIT编译器将MSIL编译为本机代码 在托管的公共语言运行库(CLR ...
- 时间的处理 --java
得到当天时间 SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd" ); String todayData = ...