Thursday, August 2, 2012

iPhone Development: Creating Native Calendar Like View

In this post I will be creating a calendar, which could reused in an any native application.
I used the calendar source from here and modified it a little bit to suit my requirements.
You can find the modified code from my project here: DOWNLOAD PROJECT
The final screen should look like this:

You can download the complete project from the link given, I will only discuss how we can reuse this calendar and modify it to fit our specific requirements.
Open the project CalendarTest in xCode. You will find a folder Calendar containing some 20 files.
You can reuse this folder as such in any project you want show the calendar. I will be using these files to display a modified calendar in CalendarTestView header and implementation files.

Open CalendarTestView.m and find the following code:

- (void)loadView {
[super loadView];
calendarView = [[[KLCalendarView allocinitWithFrame:CGRectMake(0.0f0.0f320.0f360)delegate:selfautorelease];
myTableView = [[UITableView alloc]initWithFrame:CGRectMake(0,260,320,160)style:UITableViewStylePlain];
myTableView.dataSource = self;
myTableView.delegate = self;
UIView *myHeaderView = [[UIView allocinitWithFrame:CGRectMake(0,0,myTableView.frame.size.width ,20)];
myHeaderView.backgroundColor = [UIColor grayColor];
[myTableView setTableHeaderView:myHeaderView];
[self.view addSubview:myTableView];
[self.view addSubview:calendarView];
[self.view bringSubviewToFront:myTableView];

Here I have initialized a tableView and the calendarView with appropriate frames. You can change the frames if you want to change the respective size of tableView and/or calendarView.

Now find the Calendar Delegate methods:

- (void)calendarView:(KLCalendarView *)calendarView tappedTile:(KLTile *)aTile{
NSLog(@"Date Selected is %@",[aTile date]);
[aTile flash];
if(tile == nil)
tile = aTile;
[tile restoreBackgroundColor];
tile = aTile;

Here you need to write the code that should be executed when a tile is tapped. Typical usage of this method would be to load data for the tableView from database corresponding to the date of the tile.

- (KLTile *)calendarView:(KLCalendarView *)calendarView createTileForDate:(KLDate *)date{
CheckmarkTile *tile = [[CheckmarkTile allocinit];
//tile.checkmarked = YES;//based on any condition you can checkMark a tile
return tile;

This method is called for each tile. Just like cellForRow is called for each cell. Please note that although this method will not be called more than once for a tile, but when you change the month of calendar, all tiles are redrawn, so this method will again be called for each tile.
You can find a commented line in this method! This is very important code which you can use to tell the user that they have some data corresponding to a date.
For example I have a meeting on 4th of current month. You can check if == 4th then check-mark the tile.

- (void)didChangeMonths{
UIView *clip = calendarView.superview;
if (!clip)
CGRect f = clip.frame;
NSInteger weeks = [calendarView selectedMonthNumberOfWeeks];
CGFloat adjustment = 0.f;
switch (weeks) {
case 4:
adjustment = (92/321)*360+30;
case 5:
adjustment = (46/321)*360;
case 6:
adjustment = 0.f;
f.size.height = 360 - adjustment;
clip.frame = f;
CGRect f2 = CGRectMake(0,260-adjustment,320,160+adjustment);
myTableView.frame = f2;
[self.view bringSubviewToFront:myTableView];
tile = nil;

This method is called every time you change the month in calendar. I have done some adjustments for fixing the free space between tableView and calendar when there are less number of weeks in a month. You can wrote your own logic for that.

Now the tableView delegate methods. You can write anything you want depending on your requirement. I have written some dummy code to take screen shots

#pragma mark tableViewDelegate Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell allocinitWithFrame:CGRectZero reuseIdentifier:MyIdentifier]autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[cell setText:@"No Data For Now"];
return cell;

You can read data from the source which maintains data according to the last tile tapped.

Let me know if I am missing something.

No comments:

Post a Comment