IOS自定義日歷控件的簡單實現(附思想及過程)


因為程序要求要插入一個日歷控件,該空間的要求是從當天開始及以后的六個月內的日歷,上網查資料基本上都說只要獲取兩個條件(當月第一天周幾和本月一共有多少天)就可以實現一個簡單的日歷,剩下的靠自己的簡單邏輯就OK了,下面開始自己從開始到完成的整個過程

1,首先做NSDate類目,擴展一些方法讓日期之間轉換更加方便

#import <Foundation/Foundation.h>

@interface NSDate (LYWCalendar)

#pragma mark - 獲取日
- (NSInteger)day:(NSDate *)date;
#pragma mark - 獲取月
- (NSInteger)month:(NSDate *)date;
#pragma mark - 獲取年
- (NSInteger)year:(NSDate *)date;
#pragma mark - 獲取當月第一天周幾
- (NSInteger)firstWeekdayInThisMonth:(NSDate *)date;
#pragma mark - 獲取當前月有多少天
- (NSInteger)totaldaysInMonth:(NSDate *)date;

@end

下面一一實現這些方法

#import "NSDate+LYWCalendar.h"

@implementation NSDate (LYWCalendar)

/**
 *實現部分
 */
#pragma mark -- 獲取日
- (NSInteger)day:(NSDate *)date{
    NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
    return components.day;
}

#pragma mark -- 獲取月
- (NSInteger)month:(NSDate *)date{
    NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
    return components.month;
}

#pragma mark -- 獲取年
- (NSInteger)year:(NSDate *)date{
    NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
    return components.year;
}

#pragma mark -- 獲得當前月份第一天星期幾
- (NSInteger)firstWeekdayInThisMonth:(NSDate *)date{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    //設置每周的第一天從周幾開始,默認為1,從周日開始
    [calendar setFirstWeekday:1];//1.Sun. 2.Mon. 3.Thes. 4.Wed. 5.Thur. 6.Fri. 7.Sat.
    NSDateComponents *comp = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
    [comp setDay:1];
    NSDate *firstDayOfMonthDate = [calendar dateFromComponents:comp];
    NSUInteger firstWeekday = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:firstDayOfMonthDate];
    //若設置從周日開始算起則需要減一,若從周一開始算起則不需要減
    return firstWeekday - 1;
}
#pragma mark -- 獲取當前月共有多少天

- (NSInteger)totaldaysInMonth:(NSDate *)date{
    NSRange daysInLastMonth = [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
    return daysInLastMonth.length;
}

接下來就要寫邏輯部分了,在ViewController里面實現,先說思想,首先想到的是用collectionView,但是每個月天數不一樣在日歷中的顯示就不一樣,有時候有五行有時候有六行,既然要用自動填充就必須考慮到這一點,下面是代碼實現

#import "ViewController.h"
#import "Macro.h"
#import "NSDate+LYWCalendar.h"
#import "LYWCollectionViewCell.h"
#import "LYWCollectionReusableView.h"

定義一些全局變量

static NSString *cellID = @"cellID";
static NSString *headerID = @"headerID";
static NSString *footerID = @"footerID";

@implementation ViewController
{
    //自動布局
    UICollectionViewFlowLayout *_layout;
    //表格視圖
    UICollectionView *_collectionView;
    //當月第一天星期幾
    NSInteger firstDayInMounthInWeekly;
    NSMutableArray *_firstMounth;
    //容納六個數組的數組
    NSMutableArray *_sixArray;
    
}
//定義星期視圖,若為周末則字體顏色為綠色
self.automaticallyAdjustsScrollViewInsets = NO;//關閉自動適應 NSArray *weekTitleArray = @[@"周日",@"周一",@"周二",@"周三",@"周四",@"周五",@"周六"]; for (int i = 0; i < weekTitleArray.count; i++) { UILabel *weekTitleLable = [[UILabel alloc]initWithFrame:CGRectMake(i * ((ScreenWidth/(weekTitleArray.count))), 64, ScreenWidth/(weekTitleArray.count ), 30)]; if (i == 0 || i == 6) { weekTitleLable.textColor = [UIColor greenColor]; }else{ weekTitleLable.textColor = [UIColor blackColor]; } weekTitleLable.text = [weekTitleArray objectAtIndex:i]; weekTitleLable.textAlignment = NSTextAlignmentCenter; [self.view addSubview:weekTitleLable]; }
//設置collectionView及自動布局,代理方法尤為重要
_layout = [[UICollectionViewFlowLayout alloc]init]; //頭部始終在頂端 _layout.sectionHeadersPinToVisibleBounds = YES; //頭部視圖高度 _layout.headerReferenceSize = CGSizeMake(414, 40); _layout.minimumLineSpacing = 0; _layout.minimumInteritemSpacing = 0; _collectionView = [[UICollectionView alloc]initWithFrame:CGRectMake(0, 64 + 30, ScreenWidth, ScreenHeight - 64 - 30) collectionViewLayout:_layout]; _collectionView.backgroundColor = [UIColor whiteColor]; //注冊表格 [_collectionView registerClass:[LYWCollectionViewCell class] forCellWithReuseIdentifier:cellID]; //注冊頭視圖 [_collectionView registerClass:[LYWCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerID]; //注冊尾視圖 // [_collectionView registerClass:[UICollectionReusableView class] forCellWithReuseIdentifier:footerID];
   _collectionView.delegate = self; _collectionView.dataSource = self; [self.view addSubview:_collectionView];

邏輯部分,這里有個比較長的三項表達式

(daysInMounth > 29 && (firstDayInThisMounth == 6 || firstDayInThisMounth ==5) ? 42 : 35)

就是日歷到底是六行還是七行,這就要根據日歷的特性來判斷了,如果當月天數大於29天並且當月第一天星期六(以這個程序的准則)或者星期天是返回六行剩下的返回三行,也有可能返回四行的,但是就這個程序來說是不可能的也就不需要做判斷了

 //NumberMounthes 為宏定義,表示要顯示月的個數,程序要求是六個月,所以宏定義為六

  //#define NumberMounthes 6 //想要展示的月數

//創建六個數組,並將這六個數組裝入大數組中
    _sixArray = [[NSMutableArray alloc]init];
    for (int i = 0; i < NumberMounthes ; i++ ) {
        NSMutableArray *array = [[NSMutableArray alloc]init];
        [_sixArray addObject:array];
    }
    //為六個數組寫入每個月的日歷信息
    for (int i = 0 ; i < NumberMounthes; i++) {
        //獲取月份
        int mounth = ((int)[currentDate month:currentDate] + i)%12;
        NSDateComponents *components = [[NSDateComponents alloc]init];
        //獲取下個月的年月日信息,並將其轉為date
        components.month = mounth;
        components.year = 2016 + mounth/12;
        components.day = 1;
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDate *nextDate = [calendar dateFromComponents:components];
        //獲取該月第一天星期幾
        NSInteger firstDayInThisMounth = [nextDate firstWeekdayInThisMonth:nextDate];
        //該月的有多少天daysInThisMounth
        NSInteger daysInThisMounth = [nextDate totaldaysInMonth:nextDate];
        NSString *string = [[NSString alloc]init];
        for (int j = 0; j < (daysInMounth > 29 && (firstDayInThisMounth == 6 || firstDayInThisMounth ==5) ? 42 : 35) ; j++) {
            if (j < firstDayInThisMounth || j > daysInThisMounth + firstDayInThisMounth - 1) {
                string = @"";
                [[_sixArray objectAtIndex:i]addObject:string];
            }else{
                string = [NSString stringWithFormat:@"%ld",j - firstDayInThisMounth + 1];
                [[_sixArray objectAtIndex:i]addObject:string];
            }
        }
    }

下面是代理方法

//這兩個不用說,返回cell個數及section個數
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{ return [[_sixArray objectAtIndex:section] count]; } - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{ return _sixArray.count; }
//這里是自定義cell,非常簡單的自定義
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{ LYWCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath]; UIView *blackgroundView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, cell.frame.size.width, cell.frame.size.height)]; blackgroundView.backgroundColor = [UIColor yellowColor]; cell.dateLable.text = [[_sixArray objectAtIndex:indexPath.section]objectAtIndex:indexPath.row]; NSDate *date = [[NSDate alloc]init]; NSInteger day = [date day:date];
  //設置單擊后的顏色   
cell.selectedBackgroundView = blackgroundView; return cell; }

自定義cell  .h

#import <UIKit/UIKit.h>

@interface LYWCollectionViewCell : UICollectionViewCell

@property (nonatomic,strong) UILabel *dateLable;

- (instancetype)initWithFrame:(CGRect)frame;

@end

.m

#import "LYWCollectionViewCell.h"

@implementation LYWCollectionViewCell

- (instancetype)initWithFrame:(CGRect)frame{
    if (self == [super initWithFrame:frame]) {
       _dateLable = [[UILabel alloc] initWithFrame:self.bounds];
        [_dateLable setTextAlignment:NSTextAlignmentCenter];
        [_dateLable setFont:[UIFont systemFontOfSize:17]];
        _dateLable.textColor = [UIColor blackColor];
        [self addSubview:_dateLable];
    }
    return self;
}

@end

接着代理

//cell大小及間距
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{ return CGSizeMake(ScreenWidth/7, ScreenWidth/7); } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{ return UIEdgeInsetsMake(0, 0, 0, 0); }

既然有日歷,總得顯示哪年哪月吧,前面已經注冊表頭視圖了,這里只需要實現以下代理方法即可

collectionView有點不同其頭視圖也有單獨的類,和cell一樣先自定義headCell,也是非常簡單的自定義

.h文件

#import <UIKit/UIKit.h>

@interface LYWCollectionReusableView : UICollectionReusableView

@property (nonatomic,strong) UILabel *dateLable;

- (instancetype)initWithFrame:(CGRect)frame;

@end

.m文件

#import "LYWCollectionReusableView.h"

@implementation LYWCollectionReusableView

- (instancetype)initWithFrame:(CGRect)frame{
    if (self == [super initWithFrame:frame]) {
        _dateLable = [[UILabel alloc] initWithFrame:self.bounds];
        [_dateLable setTextAlignment:NSTextAlignmentLeft];
        [_dateLable setFont:[UIFont systemFontOfSize:20]];
        _dateLable.textColor = [UIColor blackColor];
        [self addSubview:_dateLable];
    }
    return self;
}

@end

接着代理方法,這里也有個三項判斷式,和上面的大同小異,主要是防止12月顯示為0月

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
    if (kind == UICollectionElementKindSectionHeader) {
        LYWCollectionReusableView *headerRV = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:headerID forIndexPath:indexPath];
        //自定義藍色
        headerRV.backgroundColor = DODGER_BLUE;
        NSDate *currentDate = [[NSDate alloc]init];
        NSInteger year = ([currentDate month:currentDate] + indexPath.section)/12 + 2016;
        NSInteger mounth = ([currentDate month:currentDate] + indexPath.section) % 12 == 0 ? 12 : ([currentDate month:currentDate] + indexPath.section)%12;
        headerRV.dateLable.text = [NSString stringWithFormat:@"%ld年%ld月",year,mounth];
        return headerRV;
    }else{
        return nil;
    }
}

還是代理,處理選中效果,選中的為黃色

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{

    LYWCollectionViewCell *cell = [self collectionView:_collectionView cellForItemAtIndexPath:indexPath];

    NSDate *currentDate = [[NSDate alloc]init];

    //打印當前日期

    if (![cell.dateLable.text isEqualToString:@""]) {

        NSInteger year = ([currentDate month:currentDate] + indexPath.section)/12 + 2016;

        NSInteger mounth = ([currentDate month:currentDate] + indexPath.section)%12;

        NSInteger day = [cell.dateLable.text intValue];

        NSLog(@"%ld%02ld%02ld",year,mounth,day);

    }

    //排除空值cell

    //獲取月份

    NSInteger mounth = ([currentDate month:currentDate] + indexPath.section) % 12 == 0 ? 12 : ([currentDate month:currentDate] + indexPath.section)%12;

    NSDateComponents *components = [[NSDateComponents alloc]init];

    components.month = mounth;

    components.year = 2016 + mounth/12;

    components.day = 1;

    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDate *nextDate = [calendar dateFromComponents:components];

    //獲取該月第一天星期幾

    NSInteger firstDayInThisMounth = [nextDate firstWeekdayInThisMonth:nextDate];

    //該月的有多少天daysInThisMounth

    NSInteger daysInThisMounth = [nextDate totaldaysInMonth:nextDate];

    if ((indexPath.row < firstDayInThisMounth || indexPath.row > daysInThisMounth + firstDayInThisMounth - 1)){

        //如果點擊空表格則單擊無效

        [collectionView cellForItemAtIndexPath:indexPath].userInteractionEnabled = NO;

        [collectionView reloadData];

    }

}

最后展示很爛的效果圖

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM