iOS自定義日歷控件的簡(jiǎn)單實(shí)現(xiàn)過(guò)程
因?yàn)槌绦蛞笠迦胍粋€(gè)日歷控件,該空間的要求是從當(dāng)天開(kāi)始及以后的六個(gè)月內(nèi)的日歷,上網(wǎng)查資料基本上都說(shuō)只要獲取兩個(gè)條件(當(dāng)月第一天周幾和本月一共有多少天)就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的日歷,剩下的靠自己的簡(jiǎn)單邏輯就OK了,下面開(kāi)始自己從開(kāi)始到完成的整個(gè)過(guò)程:
1.首先做NSDate類目,擴(kuò)展一些方法讓日期之間轉(zhuǎn)換更加方便
#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 - 獲取當(dāng)月第一天周幾 - (NSInteger)firstWeekdayInThisMonth:(NSDate *)date; #pragma mark - 獲取當(dāng)前月有多少天 - (NSInteger)totaldaysInMonth:(NSDate *)date; @end
下面一一實(shí)現(xiàn)這些方法
#import "NSDate+LYWCalendar.h"
@implementation NSDate (LYWCalendar)
/**
*實(shí)現(xiàn)部分
*/
#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 -- 獲得當(dāng)前月份第一天星期幾
- (NSInteger)firstWeekdayInThisMonth:(NSDate *)date{
NSCalendar *calendar = [NSCalendar currentCalendar];
//設(shè)置每周的第一天從周幾開(kāi)始,默認(rèn)為1,從周日開(kāi)始
[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];
//若設(shè)置從周日開(kāi)始算起則需要減一,若從周一開(kāi)始算起則不需要減
return firstWeekday - 1;
}
#pragma mark -- 獲取當(dāng)前月共有多少天
- (NSInteger)totaldaysInMonth:(NSDate *)date{
NSRange daysInLastMonth = [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
return daysInLastMonth.length;
}
接下來(lái)就要寫(xiě)邏輯部分了,在ViewController里面實(shí)現(xiàn),先說(shuō)思想,首先想到的是用collectionView,但是每個(gè)月天數(shù)不一樣在日歷中的顯示就不一樣,有時(shí)候有五行有時(shí)候有六行,既然要用自動(dòng)填充就必須考慮到這一點(diǎn),下面是代碼實(shí)現(xiàn)
#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
{
//自動(dòng)布局
UICollectionViewFlowLayout *_layout;
//表格視圖
UICollectionView *_collectionView;
//當(dāng)月第一天星期幾
NSInteger firstDayInMounthInWeekly;
NSMutableArray *_firstMounth;
//容納六個(gè)數(shù)組的數(shù)組
NSMutableArray *_sixArray;
}
//定義星期視圖,若為周末則字體顏色為綠色
self.automaticallyAdjustsScrollViewInsets = NO;//關(guān)閉自動(dòng)適應(yīng)
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];
}
//設(shè)置collectionView及自動(dòng)布局,代理方法尤為重要 _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]; //注冊(cè)表格 [_collectionView registerClass:[LYWCollectionViewCell class] forCellWithReuseIdentifier:cellID]; //注冊(cè)頭視圖 [_collectionView registerClass:[LYWCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:headerID]; //注冊(cè)尾視圖 // [_collectionView registerClass:[UICollectionReusableView class] forCellWithReuseIdentifier:footerID]; _collectionView.delegate = self; _collectionView.dataSource = self; [self.view addSubview:_collectionView];
邏輯部分,這里有個(gè)比較長(zhǎng)的三項(xiàng)表達(dá)式
(daysInMounth > 29 && (firstDayInThisMounth == 6 || firstDayInThisMounth ==5) ? 42 : 35)
就是日歷到底是六行還是七行,這就要根據(jù)日歷的特性來(lái)判斷了,如果當(dāng)月天數(shù)大于29天并且當(dāng)月第一天星期六(以這個(gè)程序的準(zhǔn)則)或者星期天是返回六行剩下的返回三行,也有可能返回四行的,但是就這個(gè)程序來(lái)說(shuō)是不可能的也就不需要做判斷了
//NumberMounthes 為宏定義,表示要顯示月的個(gè)數(shù),程序要求是六個(gè)月,所以宏定義為六
//#define NumberMounthes 6 //想要展示的月數(shù)
//創(chuàng)建六個(gè)數(shù)組,并將這六個(gè)數(shù)組裝入大數(shù)組中
_sixArray = [[NSMutableArray alloc]init];
for (int i = 0; i < NumberMounthes ; i++ ) {
NSMutableArray *array = [[NSMutableArray alloc]init];
[_sixArray addObject:array];
}
//為六個(gè)數(shù)組寫(xiě)入每個(gè)月的日歷信息
for (int i = 0 ; i < NumberMounthes; i++) {
//獲取月份
int mounth = ((int)[currentDate month:currentDate] + i)%12;
NSDateComponents *components = [[NSDateComponents alloc]init];
//獲取下個(gè)月的年月日信息,并將其轉(zhuǎn)為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];
}
}
}
下面是代理方法
//這兩個(gè)不用說(shuō),返回cell個(gè)數(shù)及section個(gè)數(shù)
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [[_sixArray objectAtIndex:section] count];
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return _sixArray.count;
}
//這里是自定義cell,非常簡(jiǎn)單的自定義
- (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];
//設(shè)置單擊后的顏色
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);
}
既然有日歷,總得顯示哪年哪月吧,前面已經(jīng)注冊(cè)表頭視圖了,這里只需要實(shí)現(xiàn)以下代理方法即可
collectionView有點(diǎn)不同其頭視圖也有單獨(dú)的類,和cell一樣先自定義headCell,也是非常簡(jiǎn)單的自定義
.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
接著代理方法,這里也有個(gè)三項(xiàng)判斷式,和上面的大同小異,主要是防止12月顯示為0月
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
if (kind == UICollectionElementKindSectionHeader) {
LYWCollectionReusableView *headerRV = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:headerID forIndexPath:indexPath];
//自定義藍(lán)色
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];
//打印當(dāng)前日期
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)){
//如果點(diǎn)擊空表格則單擊無(wú)效
[collectionView cellForItemAtIndexPath:indexPath].userInteractionEnabled = NO;
[collectionView reloadData];
}
}
最后展示很爛的效果圖:

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS實(shí)現(xiàn)滾動(dòng)字幕的動(dòng)畫(huà)特效
這篇文章給大家?guī)?lái)一款應(yīng)用非常實(shí)用的控件,滾動(dòng)字幕,可以應(yīng)用在新聞、財(cái)經(jīng)、聊天等各類APP上,B格瞬間提升了一個(gè)檔次有木有,下面跟著小編一起看看如何實(shí)現(xiàn)的吧。2016-09-09
iOS實(shí)現(xiàn)短信驗(yàn)證碼倒計(jì)時(shí)
這篇文章主要介紹了iOS實(shí)現(xiàn)短信驗(yàn)證碼倒計(jì)時(shí)功能,一種方法是利用NSTimer計(jì)時(shí)器,另一種方法是利用GCD實(shí)現(xiàn)短信驗(yàn)證碼倒計(jì)時(shí),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
淺談iOS開(kāi)發(fā)中static變量的三大作用
下面小編就為大家?guī)?lái)一篇淺談iOS開(kāi)發(fā)中static變量的三大作用。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03
iOS開(kāi)發(fā)中不合法的網(wǎng)絡(luò)請(qǐng)求地址如何解決
這篇文章主要介紹了iOS開(kāi)發(fā)中不合法的網(wǎng)絡(luò)請(qǐng)求地址的解決方案,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
iOS系統(tǒng)和微信中不支持audio自動(dòng)播放問(wèn)題的解決方法
最近在微信端開(kāi)發(fā)H5的時(shí)候,audio標(biāo)簽在蘋(píng)果機(jī)上無(wú)法進(jìn)行自動(dòng)播放,查找相關(guān)資料終于解決了,所以下面這篇文章主要給大家介紹了關(guān)于iOS系統(tǒng)和微信中不支持audio自動(dòng)播放問(wèn)題的解決方法,需要的朋友可以參考下。2017-09-09
IOS 文件讀寫(xiě)操作詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了IOS 文件讀寫(xiě)操作詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android NavigationController 右滑手勢(shì)詳解
目前蘋(píng)果手機(jī)在人機(jī)交互中盡力做到極致,在ios7中,新增了一個(gè)小小功能,用戶不用點(diǎn)擊右上角的返回按鈕,在屏幕左邊一滑,就會(huì)返回。下面給大家詳解Android NavigationController 右滑手勢(shì),需要的朋友可以參考下2015-08-08
Objective-C的緩存框架EGOCache在iOS App開(kāi)發(fā)中的使用
這篇文章主要介紹了Objective-C的緩存框架EGOCache在iOS App開(kāi)發(fā)中的使用,重點(diǎn)講解了EGOCache對(duì)緩存過(guò)期時(shí)間的檢測(cè)及處理,需要的朋友可以參考下2016-05-05
iOS開(kāi)發(fā)中的幾個(gè)手勢(shì)操作實(shí)例分享
這篇文章主要介紹了iOS開(kāi)發(fā)中的幾個(gè)手勢(shì)操作實(shí)例分享,編寫(xiě)代碼為傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-09-09

