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目录下建立一 ...
随机推荐
- RANSAC 剔除错误匹配 估计模型
随机抽样一致,这个算法,我以前一直都没有理解透彻.只知道可以用来直线拟合,网上大多数中文博客也都是写直线拟合的,但是用来匹配二维特征的时候,总还是没弄明白. 基本概念参考 http://www.cnb ...
- 阿里im即时通讯 h5 demo
适合不想装后台环境的同学,用nodejs搭建服务器. 以下是官网提供的node 请求示例: 找到了一个ali-top-sdk 代替topClient 于是请求示例代码如下: TopClient = r ...
- OpenCV2的Mat矩阵形式自定义初始化
我们知道,OpenCV2的矩阵形式是Mat,那么Mat矩阵的初始化怎么自定义呢 ?由于比较简单,文字部分我就不多加说明了,见代码,有下面几种: //////////////////////////// ...
- adb pull apk
adb shell pm list packages adb shell pm path com.tence01.mm find -name *.apk adb pull /data/app/com. ...
- java.lang.Comparable<T> 接口
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T ...
- [Java] 特殊正则-替换字符串
public class Test { public static void main(String[] args) { String str = "2412rhttp://192.168. ...
- 005_kafka_Java_API
1.生产者Producer 1)添加依赖 <dependency> <groupId>org.apache.kafka</groupId> <artifact ...
- nodejs新建服务器
var http = require('http');// var optfile = require('./models/optfile'); http.createServer(function ...
- Sublime Text 2 代码片断
原文:Snippets 不管是在编码,还是写畅销书,你都可能会需要一遍又一遍的用到一些文本的小片断.使用片断来结束这种单调无聊的码字吧,片断是一种智能的模板,它能在合适的上下文中插入你需要的文本内容. ...
- JavaScript判断IE版本
判断IE兼容到IE11 IE浏览器与非IE浏览器的区别是IE浏览器支持ActiveXObject,但是非IE浏览器不支持ActiveXObject.在IE11浏览器还没出现的时候我们判断IE和非IE经 ...