在iOS上實現一個簡單的日曆控制項

來源:互聯網
上載者:User

近期需要寫一個互動有點DT的日曆控制項,具體互動細節這裡略過不表。

不過再怎麼複雜的控制項,也是由基礎的零配件組裝起來的,這裡最基本的就是日曆控制項。

先:


可以看出日曆控制項就是由一個個小方塊組成的,每一行有7個小方塊,分別表示一周的星期天到星期六。

給定一個月份,我們首先需要知道這個月有多少周。那麼如何確定一個月有多少周呢?

我是這麼想的,在NSDate上做擴充:

@interface NSDate (WQCalendarLogic)

0. 首先需要知道這個月有多少天:

- (NSUInteger)numberOfDaysInCurrentMonth{    // 頻繁調用 [NSCalendar currentCalendar] 可能存在效能問題    return [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;}

1. 確定這個月的第一天是星期幾。這樣就能知道給定月份的第一周有幾天:

- (NSDate *)firstDayOfCurrentMonth{    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;}- (NSUInteger)weeklyOrdinality{    return [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:self];}

2. 減去第一周的天數,剩餘天數除以7,得到倍數和餘數:

- (NSUInteger)numberOfWeeksInCurrentMonth{    NSUInteger weekday = [[self firstDayOfCurrentMonth] weeklyOrdinality];        NSUInteger days = [self numberOfDaysInCurrentMonth];    NSUInteger weeks = 0;        if (weekday > 1) {        weeks += 1, days -= (7 - weekday + 1);    }        weeks += days / 7;    weeks += (days % 7 > 0) ? 1 : 0;        return weeks;}

到這裡,就可以知道一個月有多少行多少列,從而計算出需要多少個小方塊來展示:

@interface WQCalendarTileView : UIView

這些小方塊用一個大方塊來承載:

@interface WQCalendarGridView : UIView@property (nonatomic, weak) id dataSource;@property (nonatomic, weak) id delegate;- (void)reloadData;

和UITableView類似,當WQCalendarGridView調用reloadData介面時,會開始進行布局。而布局所需要的資訊由dataSource和delegate提供:

@class WQCalendarGridView;@protocol WQCalendarGridViewDataSource @required- (NSUInteger)numberOfRowsInGridView:(WQCalendarGridView *)gridView;- (WQCalendarTileView *)gridView:(WQCalendarGridView *)gridView tileViewForRow:(NSUInteger)row column:(NSUInteger)column;@optional- (CGFloat)heightForRowInGridView:(WQCalendarGridView *)gridView;@end@protocol WQCalendarGridViewDelegate - (void)gridView:(WQCalendarGridView *)gridView didSelectAtRow:(NSUInteger)row column:(NSUInteger)column;@end

每一行的高度,heightForRow,我比較傾向於由dataSource提供 :)

第一個dataSource方法上面已經可以計算出來了,第二個dataSource方法需要對每一個tile進行配置,比如非當前月的以灰色展示,那麼我們就需要知道當前月展示中包含的 上個月殘留部分,和 下個月的開頭部分:

#pragma mark - method to calculate days in previous, current and the following month.- (void)calculateDaysInPreviousMonthWithDate:(NSDate *)date{    NSUInteger weeklyOrdinality = [[date firstDayOfCurrentMonth] weeklyOrdinality];    NSDate *dayInThePreviousMonth = [date dayInThePreviousMonth];        NSUInteger daysCount = [dayInThePreviousMonth numberOfDaysInCurrentMonth];    NSUInteger partialDaysCount = weeklyOrdinality - 1;        NSDateComponents *components = [dayInThePreviousMonth YMDComponents];        self.daysInPreviousMonth = [NSMutableArray arrayWithCapacity:partialDaysCount];    for (int i = daysCount - partialDaysCount + 1; i < daysCount + 1; ++i) {        WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];        [self.daysInPreviousMonth addObject:calendarDay];        [self.calendarDays addObject:calendarDay];    }}- (void)calculateDaysInCurrentMonthWithDate:(NSDate *)date{    NSUInteger daysCount = [date numberOfDaysInCurrentMonth];    NSDateComponents *components = [date YMDComponents];        self.daysInCurrentMonth = [NSMutableArray arrayWithCapacity:daysCount];    for (int i = 1; i < daysCount + 1; ++i) {        WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];        [self.daysInCurrentMonth addObject:calendarDay];        [self.calendarDays addObject:calendarDay];    }}- (void)calculateDaysInFollowingMonthWithDate:(NSDate *)date{    NSUInteger weeklyOrdinality = [[date lastDayOfCurrentMonth] weeklyOrdinality];    if (weeklyOrdinality == 7) return ;        NSUInteger partialDaysCount = 7 - weeklyOrdinality;    NSDateComponents *components = [[date dayInTheFollowingMonth] YMDComponents];        self.daysInFollowingMonth = [NSMutableArray arrayWithCapacity:partialDaysCount];    for (int i = 1; i < partialDaysCount + 1; ++i) {        WQCalendarDay *calendarDay = [WQCalendarDay calendarDayWithYear:components.year month:components.month day:i];        [self.daysInFollowingMonth addObject:calendarDay];        [self.calendarDays addObject:calendarDay];    }}

到此,就可以順利地展現出給定月份的日曆控制項了。最近項目比較忙,隨手記一篇 :)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.