Implement a simple calendar control on iOS

Source: Internet
Author: User

Recently, you need to write a calendar control with a little DT interaction. The interaction details are skipped here.

However, complicated controls are also assembled by basic spare parts. The most basic one here is the calendar control.

First:


It can be seen that the calendar control is composed of small blocks, each row has seven small blocks, representing the Sunday of a week to Saturday.

Given a month, we first need to know how many weeks this month has. How can we determine the number of weeks in a month?

In my opinion, extend the NSDate:

@interface NSDate (WQCalendarLogic)

0. First, you need to know the number of days in this month:

-(NSUInteger) numberOfDaysInCurrentMonth {// frequently calling [NSCalendar currentCalendar] may have performance problems return [[NSCalendar currentCalendar] rangeOfUnit: NSDayCalendarUnit inUnit: interval forDate: self]. length ;}

1. Determine the day of the week on the first day of the month. In this way, we can know that the first week of a given month has a few days:

- (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. Subtract the number of days in the first week and divide the remaining number of days by 7 to obtain the multiples and remainder:

- (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;}

Here, we can know how many rows and columns there are in a month, and then calculate the number of small squares required for display:

@interface WQCalendarTileView : UIView

These small squares are carried by a large square:

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

Similar to UITableView, when WQCalendarGridView calls the reloadData interface, the layout starts. The layout information is provided by dataSource and 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
  
 

The height of each row, heightForRow, which I prefer to provide by dataSource :)

The first dataSource method can be computed above. The second dataSource method needs to be configured for each tile. For example, the non-current month is displayed in gray, then we need to know the remaining parts of the last month included in the current month display, and the beginning of the next month:

#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];    }}

By now, you can smoothly display the calendar control for a given month. Recently, the project has been busy. Let's take a note :)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.