Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView Section Headers by month of dates in array

I have an NSArray with something similar to:

6/1/13 | Data
6/2/13 | Data
7/1/13 | Data
9/1/13 | Data

What I need to somehow get the months to create section headers - but only if they are in the array and then break the dates up into the appropriate sections. Looking like:

(Section Header)June 2013
6/1/13 | Data
6/2/13 | Data

(Section Header)July 2013
7/1/13 | Data

(skips august as no dates from august are in array)

(Section Header)September 2013
9/1/13 | Data

I am attempting to implement:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

{
    return @"June 2013";
}

But obviously need this to dynamically update with whatever months are in the array. The dates are actually NSDates that are in the array - if that makes any difference.

like image 328
user1453561 Avatar asked Jun 26 '13 21:06

user1453561


2 Answers

I have cobbled together something that should at least compile, but which is completely untested. Basically this involves pre-processing your array and storing the results in other collections that can then serve as model objects for your UITableViewDataSource.

Add these properties to the class that is your data source. You have to declare them differently if you are using ARC.

@property(retain) NSMutableArray* tableViewSections;
@property(retain) NSMutableDictionary* tableViewCells;

Add this method to your data source and make sure that you invoke it at some time before UITableView invokes your first data source method. Important: Your array must contain the NSDate objects in sorted order (the example in your question implies that this is the case).

- (void) setupDataSource:(NSArray*)sortedDateArray
{
  self.tableViewSections = [NSMutableArray arrayWithCapacity:0];
  self.tableViewCells = [NSMutableDictionary dictionaryWithCapacity:0];

  NSCalendar* calendar = [NSCalendar currentCalendar];
  NSDateFormatter* dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
  dateFormatter.locale = [NSLocale currentLocale];
  dateFormatter.timeZone = calendar.timeZone;
  [dateFormatter setDateFormat:@"MMMM YYYY"];

  NSUInteger dateComponents = NSYearCalendarUnit | NSMonthCalendarUnit;
  NSInteger previousYear = -1;
  NSInteger previousMonth = -1;
  NSMutableArray* tableViewCellsForSection = nil;
  for (NSDate* date in sortedDateArray)
  {
    NSDateComponents* components = [calendar components:dateComponents fromDate:date];
    NSInteger year = [components year];
    NSInteger month = [components month];
    if (year != previousYear || month != previousMonth)
    {
      NSString* sectionHeading = [dateFormatter stringFromDate:date];
      [self.tableViewSections addObject:sectionHeading];
      tableViewCellsForSection = [NSMutableArray arrayWithCapacity:0];
      [self.tableViewCells setObject:tableViewCellsForSection forKey:sectionHeading];
      previousYear = year;
      previousMonth = month;
    }
    [tableViewCellsForSection addObject:date];
  }
}

Now in your data source methods you can say:

- (NSInteger) numberOfSectionsInTableView:(UITableView*)tableView
{
    return self.tableViewSections.count;
}

- (NSInteger) tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
    id key = [self.tableViewSections objectAtIndex:section];
    NSArray* tableViewCellsForSection = [self.tableViewCells objectForKey:key];
    return tableViewCellsForSection.count;
}

- (NSString*) tableView:(UITableView*)tableView titleForHeaderInSection:(NSInteger)section
{
    return [self.tableViewSections objectAtIndex:section];
}

[...]

The rest of the implementation is left as an exercise to you :-) Whenever the content of your array changes you obviously need to invoke setupDataSource: to update the contents of tableViewSections and tableViewCells.

like image 55
herzbube Avatar answered Oct 18 '22 13:10

herzbube


You need to convert your existing single array and create a new array of dictionaries. Each dictionary in this new array will contain two entries - one for the month and the other entry will be an array containing the data for each row associated with the month.

If you need to add a new row to this structure, see of the month is already in the list. If so, update that month's array. Otherwise create a new dictionary with the new month and a new array containing the one new row.

like image 33
rmaddy Avatar answered Oct 18 '22 11:10

rmaddy