iOS开发一款小巧简洁的日历控件

简介: iOS开发一款小巧简洁的日历控件

iOS开发一款小巧简洁的日历控件


一、引言


       日 历是iOS开发中有时会用到的一个UI控件,网上开源的代码也很多,我浏览过一些,大致有两种模式,一种是日历的逻辑由开发者自己实现,通过计算闰年与平 年来确定月份天数,另外一种模式是通过NSDate这个时间类,来获取日历的信息。我个人认为后一种更加安全,代码性能也会更加优质,下面就是我用这种模 式实现的一个日历控件。


二、设计思路


1、先来看下效果吧


               


2、我们需要实现的功能


(1)每行7天,对应星期,列数为将当前月显示完全


(2)今日标红


(3)点击的日期背景填充


(4)提供特殊标记,用于标记计划日,节日等


(5)左右无限翻页,直到世界起源和末日


3、设计步骤


(1)设计一个日历模型


#import "YHBaseModel.h"


@interface YHBaseDateModel : YHBaseModel

@property(nonatomic,strong)NSString * year;

@property(nonatomic,strong)NSString * month;

@property(nonatomic,strong)NSString * day;

@end

(2)向系统的NSDate类中添加一些扩展方法,便于我们使用


//头文件部分

@interface NSDate (YHBaseCalendar)

/**

*获取当前月的天数

*/

- (NSUInteger)YHBaseNumberOfDaysInCurrentMonth;

/**

*获取本月第一天

*/

- (NSDate *)YHBaseFirstDayOfCurrentMonth;

//下面这些方法用于获取各种整形的数据

/**

*确定某天是周几

*/

-(int)YHBaseWeekly;

/**

*年月日 时分秒

*/

-(int)getYear;

-(int)getMonth;

-(int)getDay;

-(int)getHour;

-(int)getMinute;

-(int)getSecond;

@end


//实现部分

@implementation NSDate (YHBaseCalendar)

-(NSUInteger)YHBaseNumberOfDaysInCurrentMonth{

    return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;  

}

- (NSDate *)YHBaseFirstDayOfCurrentMonth

{

   NSDate *startDate = nil;

   BOOL ok = [[NSCalendar currentCalendar] rangeOfUnit:NSMonthCalendarUnit startDate:&startDate interval:NULL forDate:self];

   NSAssert1(ok, @"Failed to calculate the first day of the month based on %@", self);

   return startDate;

}

-(int)YHBaseWeekly{

    return (int)[[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:self];

}




-(int)getYear{

   NSCalendar *calendar = [NSCalendar currentCalendar];

   NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

   NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];

   return (int)dateComponent.year;

}

-(int)getMonth{

   NSCalendar *calendar = [NSCalendar currentCalendar];

   NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

   NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];

   return (int)dateComponent.month;

}

-(int)getDay{

   NSCalendar *calendar = [NSCalendar currentCalendar];

   NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

   NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];

   return (int)dateComponent.day;

}

-(int)getHour{

   NSCalendar *calendar = [NSCalendar currentCalendar];

   NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

   NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];

   return (int)dateComponent.hour;

}

-(int)getMinute{

   NSCalendar *calendar = [NSCalendar currentCalendar];

   NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

   NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];

   return (int)dateComponent.minute;

}

-(int)getSecond{

   NSCalendar *calendar = [NSCalendar currentCalendar];

   NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;

   NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];

   return (int)dateComponent.second;

}

@end

(3)设计我们的UI控件


//头文件部分

@interface YHBaseCalendarView : YHBaseView

@property(nonatomic,strong)NSDate * currentDate;

//标记数组 用于标记特殊日期 这个数组中存放的必须是YHBaseDateModel 对象

@property(nonatomic,strong)NSArray * markArray;

@property(nonatomic,weak)id<YHBaseCalendarViewDelegate> delegate;

@end

//实现部分

@interface YHBaseCalendarView()<UIScrollViewDelegate>

{

   //星期

   UIView * _headView;

   //日历的展示

   UIView * _bodyViewL;

   UIView * _bodyViewM;

   UIView * _bodyViewR;

   //滑动功能的支持

   UIScrollView * _scrollView;

   NSDate * _today;

 

   YHBaseDateModel * _selectModel;

}

@end

@implementation YHBaseCalendarView

-(void)reloadView{

   _currentDate = [NSDate date];

   _today = [NSDate date];

   _selectModel = [[YHBaseDateModel alloc]init];

   _selectModel.year = [NSString stringWithFormat:@"%d",[_today getYear]];

   _selectModel.month =[NSString stringWithFormat:@"%d",[_today getMonth]];

   _selectModel.day = [NSString stringWithFormat:@"%d",[_today getDay]];

   _scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 30, self.frame.size.width, self.frame.size.height)];

   _scrollView.contentSize = CGSizeMake(3*self.frame.size.width, 0);

   _scrollView.contentOffset = CGPointMake(self.frame.size.width, 0);

   _scrollView.pagingEnabled=YES;

   _scrollView.delegate=self;

   [self addSubview:_scrollView];

   _bodyViewL = [[UIView alloc]initWithFrame:CGRectMake(0, 0, _scrollView.frame.size.width, _scrollView.frame.size.height)];

   [_scrollView addSubview:_bodyViewL];

   _bodyViewM = [[UIView alloc]initWithFrame:CGRectMake(_scrollView.frame.size.width,0,  _scrollView.frame.size.width, _scrollView.frame.size.height)];

   [_scrollView addSubview:_bodyViewM];

   _bodyViewR = [[UIView alloc]initWithFrame:CGRectMake(_scrollView.frame.size.width*2, 0, _scrollView.frame.size.width, _scrollView.frame.size.height)];

   [_scrollView addSubview:_bodyViewR];

   //展示星期

   _headView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, 30)];

   _headView.backgroundColor = [UIColor redColor];

   NSArray * weekArray = @[@"SUN",@"MON",@"TUES",@"WED",@"THUR",@"FRI",@"SAT"];

   for (int i=0; i<7; i++) {

       UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(self.frame.size.width/7*i, 0, self.frame.size.width/7, 30)];

       if (i!=0&&i!=6) {

           label.backgroundColor = [UIColor redColor];

       }else{

           label.backgroundColor = [UIColor purpleColor];

       }

       label.text=weekArray[i];

       label.textAlignment = NSTextAlignmentCenter;

       label.layer.borderWidth=1;

       label.layer.borderColor = [[UIColor grayColor]CGColor];

       label.font = [UIFont boldSystemFontOfSize:16];

       label.layer.borderColor=[[UIColor grayColor] CGColor];

       label.textColor = [UIColor whiteColor];

       label.layer.borderWidth = 1;

       [_headView addSubview:label];

   }

   [self addSubview:_headView];

 

   [self creatViewWithData:_currentDate onView:_bodyViewM];

   [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];

   [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];

}

//核心的构造方法

-(void)creatViewWithData:(id)data onView:(UIView *)bodyView{

   NSDate * currentDate = (NSDate *)data;

   //获取当前月有多少天

   int monthNum = (int)[currentDate YHBaseNumberOfDaysInCurrentMonth];

   //获取第一天的日期

   NSDate * firstDate = [currentDate YHBaseFirstDayOfCurrentMonth];

   //确定这一天是周几

   int weekday = [firstDate YHBaseWeekly];

   //确定创建多少行

   int weekRow=0;

   int tmp=monthNum;

   if (weekday!=7) {

       weekRow++;

       tmp=monthNum-(7-weekday);

   }

   weekRow += tmp/7;

   weekRow += (tmp%7)?1:0;

   //开始创建按钮

   /**

    *这里的逻辑是有问题的,应该设计成cell的复用机制,而不应该重复耗性能的创建 有时间在优化

    */

#warning 可以优化哦

   NSArray * array = [bodyView subviews];

   for (UIView * v in array) {

       [v removeFromSuperview];

   }

   int nextDate = 1;

   //行

   for (int i=0; i<weekRow; i++) {

       //列

       for (int j=0; j<7; j++) {

           //先进行上个月余天的创建

           UIButton * btn;

           if (weekday!=7&&(i*7+j)<weekday) {

               //获取上个月有多少天

               NSDate * preDate = [YHBaseDateTools getPreviousframDate:currentDate];

               int preDays = (int)[preDate YHBaseNumberOfDaysInCurrentMonth];

               btn =[[UIButton alloc]initWithFrame:CGRectMake(self.frame.size.width/7*j, self.frame.size.width/7*i, self.frame.size.width/7, self.frame.size.width/7)];

               [btn setTitle:[NSString stringWithFormat:@"%d",preDays-weekday+j+1] forState:UIControlStateNormal];

               [btn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];

               [bodyView addSubview:btn];

           }else if((i*7+j+1-(weekday==7?0:weekday))<=monthNum){

               btn =[[UIButton alloc]initWithFrame:CGRectMake(self.frame.size.width/7*j, self.frame.size.width/7*i, self.frame.size.width/7, self.frame.size.width/7)];

               [btn setTitle:[NSString stringWithFormat:@"%d",(i*7+j+1-(weekday==7?0:weekday))] forState:UIControlStateNormal];

               [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

               [bodyView addSubview:btn];

           }else{

               btn =[[UIButton alloc]initWithFrame:CGRectMake(self.frame.size.width/7*j, self.frame.size.width/7*i, self.frame.size.width/7, self.frame.size.width/7)];

               [btn setTitle:[NSString stringWithFormat:@"%d",nextDate++] forState:UIControlStateNormal];

               [btn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];

               [bodyView addSubview:btn];

           }

           //将今天的日期标出

           if ([currentDate getYear]==[_today getYear]&&[currentDate getMonth]==[_today getMonth]&&[btn.titleLabel.text intValue]==[_today getDay]&&!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {

               [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];

           }

           //是否进行自定义标记

           /**

            *if中的颜色比较 是为了让上月与下月的余日不产生bug

            */

           if (_markArray!=nil) {

               for (int i=0; i<_markArray.count; i++) {

                   YHBaseDateModel * model = _markArray[i];

                   if ([currentDate getYear]==[model.year intValue]&&[currentDate getMonth]==[model.month intValue]&&[btn.titleLabel.text intValue]==[model.day intValue]&&!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {

                       btn.layer.borderColor = [[UIColor grayColor]CGColor];

                       btn.layer.borderWidth=1;

                   }

               }

           }

           //是否进行选中标记

           if ([_selectModel.year intValue]==[currentDate getYear]&&[_selectModel.month intValue]==[currentDate getMonth]&&[_selectModel.day intValue]==[btn.titleLabel.text intValue]&&!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {

               btn.backgroundColor = [UIColor cyanColor];

           }

           if (!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {

               //添加点击事件

               [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];

           }

       

       }

   }

 

}

//这个方法中进行重构

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

   if (scrollView.contentOffset.x==0) {//向前翻页了

       _currentDate = [YHBaseDateTools getPreviousframDate:_currentDate];

       _scrollView.contentOffset=CGPointMake(scrollView.frame.size.width, 0);

     

       [self creatViewWithData:_currentDate onView:_bodyViewM];

       [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];

       [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];

     

   }else if (scrollView.contentOffset.x==scrollView.frame.size.width){

     

   }else if (scrollView.contentOffset.x==scrollView.frame.size.width*2){

       _currentDate = [YHBaseDateTools getNextMonthframDate:_currentDate];

       _scrollView.contentOffset=CGPointMake(scrollView.frame.size.width, 0);

     

       [self creatViewWithData:_currentDate onView:_bodyViewM];

       [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];

       [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];

   }

   scrollView.userInteractionEnabled=YES;

   if ([self.delegate respondsToSelector:@selector(YHBaseCalendarViewScrollEndToDate:)]) {

       YHBaseDateModel * model = [[YHBaseDateModel alloc]init];

       model.year = [NSString stringWithFormat:@"%d",[_currentDate getYear]];

       model.month = [NSString stringWithFormat:@"%d",[_currentDate getMonth]];

       model.day = [NSString stringWithFormat:@"%d",[_currentDate getDay]];

       [self.delegate YHBaseCalendarViewScrollEndToDate:model];

   }

}



-(void)scrollViewDidScroll:(UIScrollView *)scrollView{

   scrollView.userInteractionEnabled=NO;

}


//点击事件

-(void)clickBtn:(UIButton *)btn{

   _selectModel.year = [NSString stringWithFormat:@"%d",[_currentDate getYear]];

   _selectModel.month = [NSString stringWithFormat:@"%d",[_currentDate getMonth]];

   _selectModel.day = btn.titleLabel.text;

   [self creatViewWithData:_currentDate onView:_bodyViewM];

   [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];

   [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];

   if ([self.delegate respondsToSelector:@selector(YHBaseCalendarViewSelectAtDateModel:)]) {

       [self.delegate YHBaseCalendarViewSelectAtDateModel:_selectModel];

   }

 

}


@end

(4)为用户交互设计的协议


@protocol YHBaseCalendarViewDelegate<NSObject>

-(void)YHBaseCalendarViewSelectAtDateModel:(YHBaseDateModel *)dateModel;

-(void)YHBaseCalendarViewScrollEndToDate:(YHBaseDateModel *)dateModel;

@end

目录
相关文章
|
11月前
|
iOS开发 开发者
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
661 67
uniapp开发ios打包Error code = -5000 Error message: Error: certificate file(p12) import failed!报错问题如何解决
|
10月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
409 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
10月前
|
人工智能 程序员 API
iOS|记一名 iOS 开发新手的前两次 App 审核经历
啥,这玩意也有新手保护期?
295 0
|
12月前
|
存储 监控 API
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
1196 11
|
物联网 Android开发 iOS开发
iOS开发 - 蓝牙学习的总结
iOS开发 - 蓝牙学习的总结
317 0
|
iOS开发
IOS开发---菜鸟学习之路--(九)-利用PullingRefreshTableView实现下拉刷新
本章主要讲解如何利用PullingRefreshTableView实现下拉(上拉)刷新的操作  PullingRefreshTableView 实现上下拉刷新的例子百度有很多,大家可以自己搜索下,先看下那些例子(一般搜索过来的都是一样的大家反正先把那部分内容先了解一下,然后再看本文档比较好。
973 0
|
iOS开发 Android开发 存储
IOS开发---菜鸟学习之路--(十)-实现新闻详细信息浏览页面
前面已经将了上下拉刷新 实现了上下拉刷新后我们的第一级界面就做好,接下来我们就需要实现 新闻详细信息浏览了 我个人认为一般实现新闻详细页面的方法有两种(主要是数据源的不同导致了方法的不同) 第一种是本身新闻就是一个链接地址,同时是已经处理好的适应手机浏览的网页 对于这种类型的数据源,我们直接在页面中放一个WebView控件,然后将URL传递过去就好了 另一种则是普通的包含标题、时间、内容、图片等数据结构的新闻内容(我们要实现的也是这种新闻,因为实现了这种之后, 我们就可以实现任何自定义的详细信息的页面了。
1021 0
|
iOS开发
IOS开发---菜鸟学习之路--(十一)-使新闻内容自适应高度
上一章当中,我们留了一个小BUG。 其实就是浏览新闻的时候,如果文字内容过长的花,UITextView 会有个下拉框,而最底层的UIScrollView也有个下拉框,那么在使用的时候就会非常的不爽。 而这章呢我们就要解决这样一个问题了 其实并不是很复杂的修改方法 我们只需要将viewDidL...
954 0
|
iOS开发
IOS开发---菜鸟学习之路--(十二)-利用ASIHTTPRequest进行异步获取数据
想要实现异步获取的话我这边了解过来有两个非常简单的方式 一个是利用ASIHTTPRequest来实现异步获取数据 另一个则是利用MBProgressHUD来实现异步获取数据 本章就先来讲解如何利用ASIHTTPRequest类来实现异步数据获取 首先大家需要百度一下ASIHTTPRequest 然后看一下百度里搜到的那些文章(不要问具体是那篇,因为我发现百度搜过来的东西全部都是一样的,所以。
1084 0
|
iOS开发
IOS开发---菜鸟学习之路--(十三)-利用MBProgressHUD进行异步获取数据
本章将介绍如何利用MBProgressHUD实现异步处理数据。 其实我本来只是像实现一个加载数据时提示框的效果,然后问了学长知道了这个类,然后就使用了 接着就发现了一个“BUG” 再然后就发现原来MBProgressHUD处理数据的时候是异步处理的 而所谓的“BUG”其实是在我实现了ASIFormDataRequest 异步处理数据后 又利用MBProgressHUD来显示加载数据框所导致的。
1155 0

热门文章

最新文章