iOS開發一款小巧簡潔的日曆控件

iOS開發一款小巧簡潔的日曆控件

1、引言

        日 歷是iOS開發中有時會用到的一個UI控件,網上開源的代碼也不少,我瀏覽過一些,大體有兩種模式,一種是日曆的邏輯由開發者本身實現,經過計算閏年與平 年來肯定月份天數,另一種模式是經過NSDate這個時間類,來獲取日曆的信息。我我的認爲後一種更加安全,代碼性能也會更加優質,下面就是我用這種模 式實現的一個日曆控件。git

2、設計思路

一、先來看下效果吧

                  

二、咱們須要實現的功能

(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

3、插個小廣告

        控件的源碼在https://github.com/ZYHshao/YHBaseFoundationTest.git中,這是我封裝的一套基於Cocoa與Foundation的更易用的開發框架,其中也對AFN,CRLabel,SDImage,MJRefresh進行了集成,有易用的下載框架,緩存框架,錯誤處理框架,皮膚管理框架等,也有支持加載HTML而且異步緩存圖片的view,邊下邊播並作緩存的AVAudioPlayer,以及各類自定義性能很強的view控件,如用block建立的按鈕,提示框以及對json和模型作相關映射的處理類,若是這些東西有幫到你,我很開心,若是你發現一些問題或者優化建議,請必定告知我,我將十分感激,QQ316045346

 

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索