Search code examples
iosuitableviewtableviewnsfetchedresultscontroller

Getting UITableView header titles from NSDate


I’m populating a tableview with data from Core Data via MagicalRecord. I want the tableview to be displayed in chronological order, sectioned by date, also in chronological order.

First, I obtain the sortDate from the transDate, from modified code I found on the web:

// Gives beginning of this day
- (NSDate *)sortDateForDate:(NSDate *)inputDate
{
    // Use the current calendar and time zone
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
    [calendar setTimeZone:timeZone];

    // Selectively convert the date components (year, month, day) of the input date
    NSDateComponents *dateComps = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:inputDate];

    // Set the time components manually
    [dateComps setHour:0];
    [dateComps setMinute:0];
    [dateComps setSecond:0];

    // Convert back
    NSDate *beginningOfDay = [calendar dateFromComponents:dateComps];

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterFullStyle];
    [formatter setTimeStyle:NSDateFormatterShortStyle];

    NSString *beginningOfDayText = [formatter stringFromDate:beginningOfDay];

    NSLog(@"The sortDate should be %@",beginningOfDayText);

    return beginningOfDay;
}

Then I assemble and save the new transaction:

-(void) assembleAndSaveTransaction
{
    NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];

    self.thisTransaction.transDate = [NSDate date];
    self.thisTransaction.paidTo = self.noteField.text;
    self.thisTransaction.forWhat = self.forWhatField.text;

    // Call to obtain sortDate, method above
    self.thisTransaction.sortDate = [self sortDateForDate:self.thisTransaction.transDate];

    [localContext MR_saveToPersistentStoreAndWait];
}

Next, I get all transactions, grouped by the attribute “sortDate” and sorted by the attribute “transDate.”

- (void)viewDidLoad
{
    [super viewDidLoad];

    transactionFRC = [WMMGTransaction MR_fetchAllGroupedBy:@"sortDate" withPredicate:nil sortedBy:@"transDate" ascending:NO];

}

Then, I convert the sortDate to a string and assign it to the header title:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterFullStyle];
    [formatter setTimeStyle:NSDateFormatterShortStyle];

    NSString *sectionLabel = [formatter stringFromDate:[transactionFRC.sections objectAtIndex:section]];

    NSLog(@"The sortDate is %@",[formatter stringFromDate:[transactionFRC.sections objectAtIndex:section]]);
    NSLog(@"The sectionLabel should be %@",sectionLabel);

    return sectionLabel;
}

Here’s how the tableview displays:

enter image description here

The data seems to be grouping and sorting correctly. Section headers appear in the chronologically appropriate places. But the header titles don't appear.

When I place this code in the viewDidLoad method of the VC with the relevant TableView:

NSLog(@"The header dates are as follows:\n");

for (NSDate *headerDate in [transactionFRC sections])
{
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterFullStyle];
    [formatter setTimeStyle:NSDateFormatterShortStyle];

    NSLog(@"The sortDate is %@",[formatter stringFromDate:headerDate]);
}

I see this readout:

2015-03-08 12:06:00.502 WheredMyMoneyGo[5374:7322757] The header dates are as follows:
2015-03-08 12:06:00.503 WheredMyMoneyGo[5374:7322757] The sortDate is (null)
2015-03-08 12:06:00.503 WheredMyMoneyGo[5374:7322757] The sortDate is (null)
2015-03-08 12:06:00.503 WheredMyMoneyGo[5374:7322757] The sortDate is (null)
2015-03-08 12:06:00.503 WheredMyMoneyGo[5374:7322757] The sortDate is (null)

I've been known to do stupid things, but I've been looking for this for 2 days without success.

Any ideas?


Solution

  • The problem here is that each section in your fetched results controller isn't a date object, as you're treating it here:

    for (NSDate *headerDate in [transactionFRC sections])
    

    Instead, [transactionFRC sections] actually returns an array of id<NSFetchedResultsSectionInfo> objects. These contain a list of objects, an object count, and a name. In your case, the name of each section will be the date in the form of a string. To test this, replace your viewDidLoad code with the following:

    for (id<NSFetchedResultsSectionInfo> section in [transactionFRC sections])
    {
        NSLog(@"The sortDate is %@", section.name);
    }
    

    However, this date isn't formatted in the style you want. You really have two options:

    1. Use your dateFormatter to first convert the section's name (string) into an NSDate object, and then to reformat that date object into a string in the format you want.

    OR

    1. In titleForHeaderInSection: fetch the first object in the section in question (if there are objects in that section), and format its sort date into a string and use that. Here's an example:

      - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
      {
          static NSDateFormatter *formatter = nil;
          if (!formatter) {
              formatter = [[NSDateFormatter alloc] init];
              [formatter setDateStyle:NSDateFormatterFullStyle];
              [formatter setTimeStyle:NSDateFormatterShortStyle];
          }
      
          id<NSFetchedResultsSectionInfo> sectionInfo = transactionFRC.sections[section];
          WMMGTransaction *transaction = [sectionInfo.objects firstObject];
      
          NSString *sectionLabel = [formatter stringFromDate:transaction.sortDate];
      
          NSLog(@"The sortDate is %@", transaction.sortDate);
          NSLog(@"The sectionLabel is %@",sectionLabel);
      
          return sectionLabel;
      }
      

    I've typed this without being able to test it in Xcode, but hopefully it'll get you somewhere.