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目录下建立一 ...
随机推荐
- iOS开发 QQ粘性动画效果
QQ(iOS)客户端的粘性动画效果 时间 2016-02-17 16:50:00 博客园精华区 原文 http://www.cnblogs.com/ziyi--caolu/p/5195615.ht ...
- java String 的+操作导致的问题
不说别的先看代码截图: 结果如下: 很好奇为什么String对象的null加上了""就等于"null"字符串了,先给点资料看看: 这个是我找的一个人博客上的截图 ...
- 联想键盘 去掉fn
这个帖子 很实用 http://iknow.lenovo.com/detail/dc_039949.html 键盘驱动下载地址:http://support1.lenovo.com.cn/lenovo ...
- javabean连数据库
1.在src下建包,然后包中建javabean类,代码如下(我的包名为aa) package aa; import java.sql.*; public class bean { private fi ...
- Windows下 Maven 使用 阿里云镜像配置
新建或者修改文件: C:\Users\user\.m2\settings.xml <settings xmlns="http://maven.apache.org/SETTINGS/1 ...
- HashMap,LinkedHashMap,TreeMap的区别
Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复. Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很 ...
- JAVA字符串05之课程问题解决
(一)古罗马皇帝凯撒在打仗时曾经使用过以下方法加密军事情报:请编写一个程序,使用上述算法加密或解密用户输入的英文字串. 1.设计思想:首先选择是加密字符串还是解密字符串,两种算法相似.如果要加密字符串 ...
- angularJS 2.0 开发的简单dome
与其说这是一个demo,不如说是一个与高手的讨教过程.虽然demo出来了,可其中有些问题我还是不太明白,如果有angularjs2.0的大神,请进来指导一番,~~~~~不甚感激. 说明第一点:我采用的 ...
- SQL Server数据库脚本备份与还原
use[master]go /*完整--备份* backup 备份 * disk='E:\USERDB.bak' 磁盘,备份文件存放的路径* name='user' 备份显示的文件名称,可随意修改* ...
- jrebel注意事项
-Xms2048m -Xmx2048m -XX:MaxPermSize=4096m -noverify -javaagent:C:\Users\Administrator\.IntelliJIdea2 ...