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];//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:1];
 NSDate *firstDayOfMonthDate = [calendar dateFromComponents:comp];
 NSUInteger firstWeekday = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:firstDayOfMonthDate];
 //若设置从周日开始算起则需要减一,若从周一开始算起则不需要减
 return firstWeekday - 1;
}
#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 = 0; i < weekTitleArray.count; i++) {
 UILabel *weekTitleLable = [[UILabel alloc]initWithFrame:CGRectMake(i * ((ScreenWidth/(weekTitleArray.count))), 64, ScreenWidth/(weekTitleArray.count ), 30)];
 if (i == 0 || i == 6) {
  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(414, 40);
 _layout.minimumLineSpacing = 0;
 _layout.minimumInteritemSpacing = 0;
 _collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 64 + 30, ScreenWidth, ScreenHeight - 64 - 30) 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 = 0; i < NumberMounthes ; i++ ) {
 NSMutableArray *array = [[NSMutableArray alloc]init];
 [_sixArray addObject:array];
 }
 //为六个数组写入每个月的日历信息
 for (int i = 0 ; i < NumberMounthes; i++) {
 //获取月份
 int mounth = ((int)[currentDate month:currentDate] + i)%12;
 NSDateComponents *components = [[NSDateComponents alloc]init];
 //获取下个月的年月日信息,并将其转为date
 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];
 NSString *string = [[NSString alloc]init];
 for (int j = 0; j < (daysInMounth > 29 && (firstDayInThisMounth == 6 || firstDayInThisMounth ==5) ? 42 : 35) ; j++) {
  if (j < firstDayInThisMounth || j > daysInThisMounth + firstDayInThisMounth - 1) {
  string = @"";
  [[_sixArray objectAtIndex:i]addObject:string];
  }else{
  string = [NSString stringWithFormat:@"%ld",j - firstDayInThisMounth + 1];
  [[_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(0, 0, 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:17]];
 _dateLable.textColor = [UIColor blackColor];
 [self addSubview:_dateLable];
 }
 return self;
}
@end

接着代理

//cell大小及间距
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
 return CGSizeMake(ScreenWidth/7, ScreenWidth/7);
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
 return UIEdgeInsetsMake(0, 0, 0, 0);
}

既然有日历,总得显示哪年哪月吧,前面已经注册表头视图了,这里只需要实现以下代理方法即可

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:20]];
 _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)/12 + 2016;
 NSInteger mounth = ([currentDate month:currentDate] + indexPath.section) % 12 == 0 ? 12 : ([currentDate month:currentDate] + indexPath.section)%12;
 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];

 }

}

最后展示很烂的效果图:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • iOS实现日历翻页动画

    本文我主要描述两方面: 1.日历(简单描述原理) 2.翻页动画(重点) 最终的效果如下图:     图中沿四个对角的翻页动画,代表对应方向手势的滑动 1. 日历 要实现一个日历,其实原理很简单,我们只要知道三个数据: 1.今天是哪一天 2.这个月的第一天是星期几(哪天) 3.这个月总共有多少天 根据这个三个数据,就可以把得到的日期显示在日历上了,至于日历用什么来显示,我个人比较喜欢用UICollectionView,一个cell代表一天,当然也可以用很多个label,button来显示. 1.获

  • iOS Swift开发之日历插件开发示例

    本文介绍了iOS Swift开发之日历插件开发示例,分享给大家,具体如下: 效果图 0x01 如何获取目前日期 关于日期,苹果给出了 Date 类,初始化一个 Date 类 let date = Date() 打印出来就是当前系统的日期和时间 那么如何单独获得当前年份,月份呢? var date: [Int] = [] let calendar: Calendar = Calendar(identifier: .gregorian) var comps: DateComponents = Dat

  • iOS My97DatePicker日历使用详解

    一.效果图 二.工程图 三.代码 RootViewController.h #import <UIKit/UIKit.h> @interface RootViewController : UIViewController @end RootViewController.m #import "RootViewController.h" //加入头文件 #import "My97DatePicker.h" @interface RootViewControl

  • iOS实现一个简易日历代码

    日历一般都是用UICollectionView进行开发的,相关demo也很多,这里就讲一个我最近写的玩的demo,由于时间原因没来得及加年历和周历,一个月历的小demo,随着月份天数的不同,自动改变日历的高. 代理部分: @protocol KJCalendarDelegate <NSObject> /** 随着每个月的天数不一样而改变高度 @param height 日历高度 */ - (void)calendarViewHeightChange:(CGFloat)height; /** 当

  • iOS自定义日历控件的简单实现过程

    因为程序要求要插入一个日历控件,该空间的要求是从当天开始及以后的六个月内的日历,上网查资料基本上都说只要获取两个条件(当月第一天周几和本月一共有多少天)就可以实现一个简单的日历,剩下的靠自己的简单逻辑就OK了,下面开始自己从开始到完成的整个过程 1.首先做NSDate类目,扩展一些方法让日期之间转换更加方便 #import <Foundation/Foundation.h> @interface NSDate (LYWCalendar) #pragma mark - 获取日 - (NSInte

  • iOS 简约日历控件EBCalendarView的实现代码

    本文介绍了iOS 简约日历控件EBCalendarView的实现代码,分享给大家,具体如下: EBCalendarView日历控件,调用简单,代码简洁. github地址:https://github.com/woheduole/EBCalendarView 效果图 调用示例 EBCalendarView *calendarView = [[EBCalendarView alloc] initWithFrame:CGRectMake(0, 64, CGRectGetWidth(self.view

  • Android使用GridLayout绘制自定义日历控件

    效果图 思路:就是先设置Gridlayout的行列数,然后往里面放置一定数目的自定义日历按钮控件,最后实现日历逻辑就可以了. 步骤: 第一步:自定义日历控件(初步) 第二步:实现自定义单个日期按钮控件 第三步:将第二步得到的控件动态添加到第一步的布局中,并实现日期逻辑 第四步:编写单个日期点击监听器接口 第一步:自定义日历控件(初步) <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmln

  • Android自定义日历控件实例详解

    为什么要自定义控件 有时,原生控件不能满足我们对于外观和功能的需求,这时候可以自定义控件来定制外观或功能:有时,原生控件可以通过复杂的编码实现想要的功能,这时候可以自定义控件来提高代码的可复用性. 如何自定义控件 下面我通过我在github上开源的Android-CalendarView项目为例,来介绍一下自定义控件的方法.该项目中自定义的控件类名是CalendarView.这个自定义控件覆盖了一些自定义控件时常需要重写的一些方法. 构造函数 为了支持本控件既能使用xml布局文件声明,也可在ja

  • Android实现可滑动的自定义日历控件

    最近用到的一个日历控件,记录下,效果如图 代码下载地址:点击打开链接 布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical&q

  • iOS开发中使用Quartz2D绘图及自定义UIImageView控件

    绘制基本图形 一.简单说明 图形上下文(Graphics Context):是一个CGContextRef类型的数据 图形上下文的作用:保存绘图信息.绘图状态 决定绘制的输出目标(绘制到什么地方去?)(输出目标可以是PDF文件.Bitmap或者显示器的窗口上) 相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上. Quartz2D提供了以下几种类型的Graphics Context: Bitmap Graphics Context PDF Grap

  • Android自定义实现日历控件

    本文实例为大家分享了Android自定义实现日历控件的具体代码,供大家参考,具体内容如下 1. Calendar类 2. 布局 创建calendar_layout.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:padding="20sp" android:orientation="vertical" android:l

  • Bootstrap DateTime Picker日历控件简单应用

    一个日历控件,这是官方说明,,供大家参考,具体内容如下 首先引入css样式 <!--引入bootstrap 和bootstrap-datetimepicker样式表--> <link rel="stylesheet" href="bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="external nofollow" /> <link rel="styleshe

  • Android 一个日历控件的实现代码

    先看几张动态的效果图吧! 项目地址:https://github.com/Othershe/CalendarView 这里主要记录一下在编写日历控件过程中一些主要的点: 一.主要功能 1.支持农历.节气.常用节假日 2.日期范围设置,默认支持的最大日期范围[1900.1~2049.12] 3.默认选中日期设置 4.单选.多选 5.跳转到指定日期 6.通过自定义属性定制日期外观,以及简单的日期item布局配置 二.基本结构 我们要实现的日历控件采用ViewPager作为主框架,CalendarVi

  • JS学习之一个简易的日历控件

    这个日历控件类似于园子用的日历,如下图: 这种日历控件实现起来不难,下面简单分析下我的思路: 首先,是该控件的可配置项: 复制代码 代码如下: ... settings: { firstDayOfWeek: 1, baseClass: "calendar", curDayClass: "curDay", prevMonthCellClass: "prevMonth", nextMonthCellClass: "nextMonth&quo

随机推荐