uitableviewnsfetchedresultscontrollerdate-sorting

Sorting with NSFetchedResultsController


Using XCode 4.6, iOS6, CoreData, UITableViewController, NSFetchesResultsController

I figured out how to get the following dates in to a NSDate variables:

todayDate
yesterdayDate
thisWeek
lastWeek
thisMonth
lastMonth
lastYear

Now I would like to use NSFetchedResultsController to put the data into sections based on the data in the above variables. I'm assuming I will have to do some date compare:

if ([date1 compare:date2] == NSOrderedDescending) {
NSLog(@"date1 is later than date2");        
} else if ([date1 compare:date2] == NSOrderedAscending) {
    NSLog(@"date1 is earlier than date2");
} else {
    NSLog(@"dates are the same");
}

There is possibility that some users will not have some of the sections. So, I need help with what to put in the following method to determine the number of sections based on the fetchRequest:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

I also need to know how to divide the data into cells numberOfRowsInSection: and have it displayed into cells cellforRowAtIndexPath:

Some code examples would be nice! Thanks.

EDIT

I haven't integrated the controller yet, but below is the code to fetch:

- (void) refreshTable {

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Meeting" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];

    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastmoddate" ascending:NO];
    NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];

    [fetchRequest setSortDescriptors:sortDescriptors];

    [self.managedObjectContext executeFetchRequest:fetchRequest onSuccess:^(NSArray *results) {
        [self.refreshControl endRefreshing];
        self.objects = results;
        [self.tableView reloadData];

    } onFailure:^(NSError *error) {

        [self.refreshControl endRefreshing];
        NSLog(@"An error %@, %@", error, [error userInfo]);
    }];

Here is how I'm getting the dates:

   NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *components = [cal components:( NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:[[NSDate alloc] init]];

    [components setHour:-[components hour]];
    [components setMinute:-[components minute]];
    [components setSecond:-[components second]];
    NSDate *today = [cal dateByAddingComponents:components toDate:[[NSDate alloc] init] options:0]; //This variable should now be pointing at a date object that is the start of today (midnight);

    [components setHour:-24];
    [components setMinute:0];
    [components setSecond:0];
    NSDate *yesterday = [cal dateByAddingComponents:components toDate: today options:0];

    components = [cal components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:[[NSDate alloc] init]];

    [components setDay:([components day] - ([components weekday] - 1))];
    NSDate *thisWeek  = [cal dateFromComponents:components];

    [components setDay:([components day] - 7)];
    NSDate *lastWeek  = [cal dateFromComponents:components];

    [components setDay:([components day] - ([components day] -1))];
    NSDate *thisMonth = [cal dateFromComponents:components];

    [components setMonth:([components month] - 1)];
    NSDate *lastMonth = [cal dateFromComponents:components];

Solution

  • This is what I would do. Create a core data model and 'user' entity within that model. I assume u know how to do this, but if u dont just comment and ill explain. Then just create a custom class for ur entity like you would with any other entity and give it the lastmoddate NSDate attribute, plus I would add a creationDate attribute which will be equal to the exact date and time of creating the entity so that you can use this as an identifier for each UserEntity. You will also have to have an array of separate arrays for each section you want to create in your UITableView. When you add a user entity to your model, set the attributes, and then because you said some users might not have every variable, set any variable that the user entity doesn't need to nil. When you initialize your arrays just add the user entities who have the appropriate attributes set to a value. Finally, when you implement -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView all you have to do is count the arrays.

    Here is what it should look like:

    Im going to call the entity that represents each user 'UserEntity' (its NSManagedObject s class will have the same name).

    The following code goes in the header of which ever class you are going to use to create and fetch entities - it will probably same class you are using to display the UITableView.

    @interface Class : UITableViewController {
        NSManagedObjectContext *managedObjectContext;
        NSMutableArray *arrayOfArrays;
    }
    
    @property (retain, nonatomic) NSManagedObjectContext   *managedObjectContext;
    @property (retain, nonatomic) NSMutableArray   *arrayOfArrays;
    

    The following code goes in the implementation of which ever class you are going to use to create and fetch entities - it will probably same class you are using to display the UITableView.

    //for adding the UserEntity to the model (core data database) and setting its attributes

    - (UserEntity *)addUserEntityToModel {
         UserEntity *myEnt = (UserEntity *)[NSEntityDescription insertNewObjectForEntityForName:@"UserEntity" inManagedObjectContext:self.managedObjectContext];
    
       myEnt.creationDate = [NSDate date];
       myEnt. lastmoddate = ...; //replace the '...' with however you get your date
       //add any other values to the appropriate attributes here, any attributes that do not //pertain to the UserEntity simply ignore - they are automatically set to default when they //are created.
    
        NSError *error = nil;
        if(!managedObjectContext) 
            NSLog(@"managedObejctContext problem at ...");
        else if (![managedObjectContext save:&error]) {
            NSLog(@"context not saved!");;
        } else 
        NSLog(@"context successfully saved.");      
    }
    

    //for getting the UserEntity's

    - (NSMutableArray *)getFetchArray {
    
            NSFetchRequest *request = [[NSFetchRequest alloc] init];
            if(!managedObjectContext) {
                NSLog(@"There is no managedObjectContext at getFetchArray");
            }
               NSEntityDescription *entity = [NSEntityDescription entityForName:@"UserEntity" inManagedObjectContext:managedObjectContext];
    
            [request setEntity:entity];
    
            NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"creationDate" ascending:NO];
            NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
            [request setSortDescriptors:sortDescriptors];
            [sortDescriptors release];
            [sortDescriptor release];
    
            NSError *error = nil;
            NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
            if (mutableFetchResults == nil) {
                NSLog(@"mutableFetchResults array is nil");
            } 
            [request release];
    
            return mutableFetchResults;
        }
    

    //setting up arrayOfArrays

    -(void)createArrayOfArrays {
     NSMutableArray *fetchArray = [self getFetchArray];
      NSMutableArray *todayDateArray = [[NSMutableArray alloc] init];
      NSMutableArray *yesterdayDateArray = [[NSMutableArray alloc] init];
      NSMutableArray *thisWeekArray = [[NSMutableArray alloc] init];
            //... create an array for each section you want
    
    for (UserEntity *ue in fetchArray) {
           if(ue.lastmoddate) { //if the attribute is not nil
            if(ue.lastmoddate isEqual todayDate)
           [todayDateArray insertObject:ue atIndex:0];
         else if (ue.lastmoddate isEqual yesterdayDate)
           [yesterdayDateArray insertObject:ue atIndex:0];
            // ... do this for every section array 
            }
        }
    
     arrayOfArrays = [[NSMutableArray alloc] initWithObjects:todayDateArray,yesterdayDateArray,...(the rest of the arrays created for each attribute), nil]
    
    
    
      [todayDateArray release];
        [yesterdayDateArray release];
      //  ... release all the arrays except arrayOfArrays and fetchArray     
     }
    

    //setting up the managedObjectContext

    - (id)init {
        self = [super init];
    
        if (self) {
        AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];//what ever //the name of your appDel is.
            NSManagedObjectContext *context = [appDelegate managedObjectContext];
            self.managedObjectContext = context;
    
           [self createArrayofArrays];//this first creates the arrayOfArrays and will set it //equal to anything that's saved in the database. If you add an entity you will need to update //it.
        }
    
        return self;
    }
    
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        // Return the number of sections.
        return arrayOfArrays.count;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        // Return the number of rows in the section.
        return [[arrayOfArrays objectAtIndex:section] count];
    }
    

    //then displaying the cells in your table view

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
        {
            static NSString *CellIdentifier = @"Cell";
    
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
            if (!cell) {
                cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
            }
    
    
            UserEntity *ent = (UserEntity *)[[arrayOfArrays objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
            cell.accessoryType = UITableViewCellAccessoryBasic;
            if(ent.lastmoddate) { //if lastmoddate is not nil
            cell.textLabel.text = ent. lastmoddate;
            } else {
                NSLog(@"lastmoddate is nil at tableView: cellForRowAtIndexPath");
            }
    
            NSLog(@"tableView: cellForRowAtIndexPath called.");
            return cell;
        }