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
三、插个小广告
控件的源码在https://github.com/ZYHshao/YHBaseFoundationTest.git 中 ,这是我封装的一套基于Cocoa与Foundation的更易用的开发框架,其中也对AFN,CRLabel,SDImage,MJRefresh进行了集成,有易用的下载框架,缓存框架,错误处理框架,皮肤管理框架等,也有支持加载HTML并且异步缓存图片的view,边下边播并做缓存的AVAudioPlayer,以及各种自定义性能很强的view控件,如用block创建的按钮,提示框以及对json和模型做相关映射的处理类,如果这些东西有帮到你,我很开心,如果你发现一些问题或者优化建议,请一定告知我,我将十分感激,QQ316045346
文章转载自 开源中国社区[https://www.oschina.net]

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
Fiddler抓包使用教程-Https
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/72956016 本文出自【赵彦军的博客】 开启 Https 抓包 Fiddler 默认下,Fiddler不会捕获HTTPS会话,需要你设置下。 from all processes : 抓取所有的 https 程序, 包括 本机 和 手机 from browsers only : 只抓取浏览器中的 https 请求 from non-browsers only : 只抓取除了浏览器之外的所有 https 请求 from remote clients only : 抓取远程的客户端的 https ,可以代表手机 开启手机 Https 抓包 如果只想抓取 手机上的所有 https 请求,就应该选中 from remote clients only 。 【浅谈HTTPS以及Fiddler抓取HTTPS协议】 如何用Fiddler对Android应用进行抓包 http://jingyan.baidu.com/article/03b2f78c7b6bb05ea237aed2.htm...
-
下一篇
Objective-C:MRC(引用计数器)在OC内部的可变对象是适用的,不可变对象是不适用的(例如 NSString、NSArray等)
引用计数和字符串 内存中的常量字符串的空间分配与其他对象不同,他们没有引用计数机制 凡是自定义的对象都有引用计数机制; OC内部中对象分为可变对象(NSMutableString等)和不可变对象(NSString、NSArray等), 不可变对象不适用于引用计数的机制,可变的对象适用引用计数机制。 1 // main.m 2 // 03-unmutableobject 3 // 4 // Created by ma c on 15/8/13. 5 // Copyright (c) 2015年. All rights reserved. 6 // 7 8 #import <Foundation/Foundation.h> 9 10 int main(int argc, const char * argv[]) 11 { 12 @autoreleasepool 13 { 14 //不可变对象不适用于引用计数 15 //1.创建3个对象 16 NSString *str1 = @"hello world";//不可变对象 17 18 NSString *str2 = [NS...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- MySQL数据库在高并发下的优化方案
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8编译安装MySQL8.0.19
- Dcoker安装(在线仓库),最新的服务器搭配容器使用