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

 更新时间:2021年08月25日 14:22:10   作者:小姐贫僧光天化日  
这篇文章主要为大家详细介绍了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];

 }

}

最后展示很烂的效果图:

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

相关文章

  • iOS实现滚动字幕的动画特效

    iOS实现滚动字幕的动画特效

    这篇文章给大家带来一款应用非常实用的控件,滚动字幕,可以应用在新闻、财经、聊天等各类APP上,B格瞬间提升了一个档次有木有,下面跟着小编一起看看如何实现的吧。
    2016-09-09
  • iOS实现短信验证码倒计时

    iOS实现短信验证码倒计时

    这篇文章主要介绍了iOS实现短信验证码倒计时功能,一种方法是利用NSTimer计时器,另一种方法是利用GCD实现短信验证码倒计时,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • 浅谈iOS开发中static变量的三大作用

    浅谈iOS开发中static变量的三大作用

    下面小编就为大家带来一篇浅谈iOS开发中static变量的三大作用。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • iOS开发中不合法的网络请求地址如何解决

    iOS开发中不合法的网络请求地址如何解决

    这篇文章主要介绍了iOS开发中不合法的网络请求地址的解决方案,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-09-09
  • iOS系统和微信中不支持audio自动播放问题的解决方法

    iOS系统和微信中不支持audio自动播放问题的解决方法

    最近在微信端开发H5的时候,audio标签在苹果机上无法进行自动播放,查找相关资料终于解决了,所以下面这篇文章主要给大家介绍了关于iOS系统和微信中不支持audio自动播放问题的解决方法,需要的朋友可以参考下。
    2017-09-09
  • IOS 文件读写操作详解及简单实例

    IOS 文件读写操作详解及简单实例

    这篇文章主要介绍了IOS 文件读写操作详解及简单实例的相关资料,需要的朋友可以参考下
    2017-04-04
  • Android NavigationController 右滑手势详解

    Android NavigationController 右滑手势详解

    目前苹果手机在人机交互中尽力做到极致,在ios7中,新增了一个小小功能,用户不用点击右上角的返回按钮,在屏幕左边一滑,就会返回。下面给大家详解Android NavigationController 右滑手势,需要的朋友可以参考下
    2015-08-08
  • iOS CoreData 增删改查详解

    iOS CoreData 增删改查详解

    这篇文章主要为大家详细介绍了iOS CoreData 增删改查的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-09-09
  • Objective-C的缓存框架EGOCache在iOS App开发中的使用

    Objective-C的缓存框架EGOCache在iOS App开发中的使用

    这篇文章主要介绍了Objective-C的缓存框架EGOCache在iOS App开发中的使用,重点讲解了EGOCache对缓存过期时间的检测及处理,需要的朋友可以参考下
    2016-05-05
  • iOS开发中的几个手势操作实例分享

    iOS开发中的几个手势操作实例分享

    这篇文章主要介绍了iOS开发中的几个手势操作实例分享,编写代码为传统的Objective-C,需要的朋友可以参考下
    2015-09-09

最新评论