日 歷是iOS開發中有時會用到的一個UI控件,網上開源的代碼也不少,我瀏覽過一些,大體有兩種模式,一種是日曆的邏輯由開發者本身實現,經過計算閏年與平 年來肯定月份天數,另一種模式是經過NSDate這個時間類,來獲取日曆的信息。我我的認爲後一種更加安全,代碼性能也會更加優質,下面就是我用這種模 式實現的一個日曆控件。git
(1)每行7天,對應星期,列數爲將當前月顯示徹底github
(2)今日標紅json
(3)點擊的日期背景填充數組
(4)提供特殊標記,用於標記計劃日,節日等緩存
(5)左右無限翻頁,直到世界起源和末日安全
(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控件ide
//頭文件部分 @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
專一技術,熱愛生活,交流技術,也作朋友。
——琿少 QQ羣:203317592