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
來自:http://my.oschina.net/u/2340880/blog/502361
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!