如果想做一個日歷(iOS)

  • 寫一個日歷首先你要用到 NSCalendar類,這是一個用來處理日歷相關的類,這個類中官方提供了很多計算日期的方法。尤其iOS8以后關于日期的計算更為詳細,具體方法可以點進類中一一查看。
  • NSDateComponents日期組件類,通過這個類你可以拿到某個日期或者某段日期你所想要的信息,比如某個日期是本月第幾周、周幾等等,都可以將NSDate通過NSCalendar轉化為該類拿到。

使用這兩個類就可以完成一個簡單日歷的日期處理了

  • 接下來我們就一步步來實現一個日歷
  • 我們理一下思路,跟著思路一步步去做
    任何控件都由兩部分組成UI展示邏輯實現

UI:我們用一個UICollectionView作為我們日歷實現的基礎控件,日歷都是一個月作為一個單位,我們可以按照這個思路來將每月作為collectionView的一個section來處理,section中的每一個item為該月具體的某一天

邏輯:
1.一個日歷必須有一個起始時間和一個結束時間,為什么這兩個必須有呢?因為宇宙萬物皆有始有終..,我們每個section對應一個月,我們從何而起,又從何結束呢?
2.我們需要精準的將每一個日期和每一個item對應起來

UI方面就不過多贅述了,沒別的東西就一個單純的collectionView...

邏輯方面需要處理

一共多少個分區
- (NSInteger)numberOfSections;
每個分區有多少個item
- (NSInteger)numberOfItemInSection:(NSInteger)section;
每個item對應的日期
- (NSDate *)dateWithIndexPath:(NSIndexPath *)indexPath;

當然這些方法需要我們一個個實現

//先來第一個 分區數

//分區數(在collectionView返回分區數的代理方法中調用即可)
- (NSInteger)numberOfSections{
  return [self numberOfMonths]
}

//private methods
//總月份
- (NSInteger)numberOfMonths{
   NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
   //去除計算月份差時日期分秒等干擾信息 _minimumDate _maximumDate 為預先設置的起始時間和結束時間
   NSDate *fromDate;
   NSDate *toDate;
   [calendar rangeOfUnit:NSCalendarUnitMonth startDate:&fromDate interval:NULL forDate: _minimumDate];
   [calendar rangeOfUnit:NSCalendarUnitMonth startDate:&toDate interval:NULL forDate: _maximumDate];

   NSDateComponents *dateComponents = [calendar components:NSCalendarUnitMonth fromDate:fromDate toDate:toDate options:NSCalendarMatchStrictly];
   return dateComponents.month + 1;
}
//第二部分 開始計算每個分區有多少個item

//當前分區的item個數
- (NSInteger)numberOfItemInSection:(NSInteger)section{
   //當前月
   NSDate *currentMonth =  [self monthWithSection:section];
   //當前月最后一天
   NSDate *lastDayInMonth = [self lastDayOfMonth:currentMonth];
   //日期組件對象
   NSDateComponents *lastDateComponents = [self dateComponents:lastDayInMonth];
   return  lastDateComponents.weekOfMonth * 7;
}

//private methods
//當前是那個月
- (NSDate *)monthWithSection:(NSInteger)section{
   NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
   //得到月份差值的組件對象
   NSDateComponents *someDate = [[NSDateComponents alloc] init];
   [someDate setYear:0];
   [someDate setMonth: section];
   [someDate setDay:0];
    
   NSDate *resultDate = [calnedar dateByAddingComponents:someDate toDate: _minimumDate options:NSCalendarMatchStrictly];
   return resultDate;
}

//當月第一天
- (NSDate *)firstDayOfMonth:(NSDate *)month{
   if(month == nil) return;
   NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
   NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitWeekday|NSCalendarUnitWeekOfMonth fromDate:month];
   components.day = 1;
   return [calendar dateFromComponents:components];
}

//當月最后一天
- (NSDate *)lastDayOfMonth:(NSDate *)month{
   if(month == nil) return;
   NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
   NSDateComponents *components = [calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitWeekday|NSCalendarUnitWeekOfMonth fromDate:month];
   components.month++;
   components.day = 0;
   return [calendar dateFromComponents:components];
}

//獲取日期組件對象
- (NSDateComponents *)dateComponents:(NSDate *)date{
   NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
   NSDateComponents *dateComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday | NSCalendarUnitWeekOfMonth fromDate:date];
   return dateComponents;
}

//最后一步了 此方法可將collectionView中的item和日期相對應起來了

//根據indexPath獲取當前date
- (NSDate *)dateWithIndexPath:(NSIndexPath *)indexPath{

   NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];

   //當前的月份(第二部分中的方法)
   NSDate *currentMonth = [self monthWithSection:indexPath.section];
   //當月第一天和最后一天(第二部分中的方法)
   NSDate *fristDayInMonth = [self firstDayOfMonth:currentMonth];
   NSDate *lastDayInMonth = [self lastDayOfMonth:currentMonth];

   //使用的最小最大時間
   NSDate *usedMinDate = fristDayInMonth.copy;
   NSDate *usedMaxDate = lastDayInMonth.copy;

   //日歷第一個月和最后一個月有可能不是當月第一天或者最后一天開始或結束的處理(比如_minimunDate為 2016-03-05 則本月日期從5號開始顯示)
   if (indexPath.section == 0) {
       usedMinDate = [calendar numberOfDaysFromDate:usedMinDate toDate:_minimumDate] > 0 ? _minimumDate : usedMinDate;
    }
   NSInteger numberOfMonths = [self numberOfMonths];
   if (indexPath.section == numberOfMonths - 1) {
       usedMaxDate = [calendar numberOfDaysFromDate:usedMaxDate toDate:_maximumDate] > 0 ? usedMaxDate : _maximumDate;
    }
   NSDateComponents *uMinCompontents = [calendar dateComponents:usedMinDate];
   NSDateComponents *uMaxCompontents = [calendar dateComponents:usedMaxDate];

    //section中最小沒有日期的天數
   NSInteger minNoDateCount = (uMinCompontents.weekOfMonth - 1) * 7 + uMinCompontents.weekday - 1;
   if (indexPath.row < minNoDateCount) {
       return nil;
    }
   //最大沒有日期的天數
   NSInteger maxNoDateCount = (uMaxCompontents.weekOfMonth - 1) * 7 + uMaxCompontents.weekday - 1;
    if (indexPath.row > maxNoDateCount) {
        return nil;
    }

    //當前item對應的日期
   NSDate *itemDate = [calendar dateWithDifferenceDays:indexPath.row - minNoDateCount atDate:usedMinDate];
   return itemDate;
}

//private methods
//兩個日期相差的天數
- (NSInteger)numberOfDaysFromDate:(NSDate *)startDate toDate:(NSDate *)endDate
{
   NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
   //去掉時分秒信息
   NSDate *fromDate;
   NSDate *toDate;
   [calendar rangeOfUnit:NSCalendarUnitDay startDate:&fromDate interval:NULL forDate:startDate];
   [calendar rangeOfUnit:NSCalendarUnitDay startDate:&toDate interval:NULL forDate:endDate];
    
   NSDateComponents *dateComponents = [calendar components:NSCalendarUnitDay fromDate:fromDate toDate:toDate options:NSCalendarMatchStrictly];
   return dateComponents.day;
}

到這里一個日歷的基本顯示應該沒有什么問題了,如果你想做更多復雜的功能便可以依賴collectionView自己去實現了。。。上文很多代碼是手打的可能會有一些問題。。。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內容