views:

78

answers:

1

I have a tableview that is successfully incorporating an NSFetchedResultsController. However, I need the topmost cell in my tableview to read, "Add new object" and have UITableViewCellEditingStyleInsert instead of the default UITableViewCellEditingStyleDelete.

The FetchResultsController wants to check the managedObjectContext for objects--both to determine number of rows and to populate the table cells. The only way I can think to get around this is to create a dummy object, but I feel like there ought to be a more elegant solution.

UPDATE:

For those who might be curious as to what solution I ended up with, I decided to have my insert cell at the bottom, not the top. Here is the relevant code:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Uncomment the following line to preserve selection between presentations.
    //self.clearsSelectionOnViewWillAppear = NO;

    self.editing = YES;
    self.tableView.allowsSelectionDuringEditing = YES;
    self.tableView.delegate = self;

    RubricAppDelegate *appDelegate = (RubricAppDelegate *)[[UIApplication sharedApplication] delegate];
    managedObjectContext = [appDelegate managedObjectContext];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"myClass" inManagedObjectContext:managedObjectContext];
    NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
    [request setEntity:entity];

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"classID" ascending:YES];
    NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
    [request setSortDescriptors:sortDescriptors];
    [sortDescriptor release];

    fetchedResultsController = [[NSFetchedResultsController alloc]
                                               initWithFetchRequest:request 
                                               managedObjectContext:self.managedObjectContext
                                               sectionNameKeyPath:nil cacheName:nil];
    NSError *error;
    [fetchedResultsController performFetch:&error];

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSLog(@"Number of sections = %d", [[fetchedResultsController sections] count]);

    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    // Return the number of rows in the section.
    id <NSFetchedResultsSectionInfo> myClass = [[fetchedResultsController sections] objectAtIndex:section];
    NSLog(@"Number of classes = %d", [myClass numberOfObjects]);

    return ([[fetchedResultsController fetchedObjects] count] + 1);

}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    NSLog(@"FRC count + 1 = %d", ([[fetchedResultsController fetchedObjects] count] + 1));

    if (indexPath.row == ([[fetchedResultsController fetchedObjects] count])) {
        cell.textLabel.text = @"Add New Class";
    }
    else {
        myClass *theClass = [fetchedResultsController objectAtIndexPath:indexPath];
        NSLog(@"Class name is: %@", theClass.classTitle);
        cell.textLabel.text = theClass.classTitle;
    }


    return cell;
}

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == [[fetchedResultsController fetchedObjects] count]) {
        return UITableViewCellEditingStyleInsert;
    }
    return UITableViewCellEditingStyleDelete;
}

The result (with some junk data):

alt text

Now my only issue is getting the delete functions to work properly. You can follow my post on that issue here

+1  A: 

Normally the add row is at the bottom.

You can accomplish this by changing the -tableView:numberOfRowsInSection: and the -tableView:cellForRowAtIndexPath: methods to adjust the cell count and adjust for it. So your -tableView:numberOfRowsInSection: would return N+1 and your -tableView:cellForRowAtIndexPath: would get object at N-1 unless N == 0 then it would return your "Add new object" cell.

There is no need to mess with the underlying Core Data elements as this is strictly a UI issue.

Update

But now I'm not sure how to return the count of my fetched objects (assuming that is what I used for "N" in your above answer). Also, wouldn't I want -tableView:cellForRowAtIndexPath to return my "Add new object" cell when the indexPath.row = (N + 1), not N = 0? I may be misunderstanding what "N" equates to, but I thought it just meant count of fetched objects.

Yes it is the count of actual objects.

You do want your -tableView:cellForRowAtIndexPath: to return a cell for your "Add new object" otherwise what is the point? You just want it to return a different type of cell.

All you are doing in this solution is adding a cell that is not part of the NSFetchedResultsController and then compensating for it when you are retrieving an actual object from the NSFetchedResultsController and when the user selects a cell.

Marcus S. Zarra
This may deserve another post, but one odd thing about NSFetchedResultsController (at least as I have it set up) is each object is returned in its own section. I would prefer it if there were only one section and each object were assigned to a different row, but if I set -numberOfSectionsInTableView to return 1, it only displays one object and cuts the rest out. Given that fact, I have tried to return N+1 for my sections and N-1 for object retrieval, but its still a no-go.
Spindler
Create a new post for that and show your code. Sounds like something is wrong either in your config of the controller or in your datasource delegate methods.
Marcus S. Zarra
Marcus, I would upvote your answer if my reputation were high enough :P After posting another question, I got my sections set to 1 and my objects returning to that section's rows. I didn't have my sectionNameKeyPath set to nil. But now I'm not sure how to return the count of my fetched objects (assuming that is what I used for "N" in your above answer). Also, wouldn't I want -tableView:cellForRowAtIndexPath to return my "Add new object" cell when the indexPath.row = (N + 1), not N = 0? I may be misunderstanding what "N" equates to, but I thought it just meant count of fetched objects.
Spindler
You can accept the answer since you are the OP.
Marcus S. Zarra
I apologize for being so slow on the uptake. I hadn't marked your response as an answer yet because I hadn't been able to verify it as working yet. Well, after my 5th or 6th time going through the code, I found my error in implementing it and it is working now! It's the small victories for a beginner like me :P But in the process of searching for an answer, I came across a solution which sets the insert cell as the tableFooterView, thereby eliminating the need to mess with the FRC (in cellForRowAtIndexPath). Would you recommend against that for any reason?
Spindler
I personally prefer it as it produces cleaner code. You can also put it in the header if you want it at the top.
Marcus S. Zarra
Mr. Zarra, thank you so much for your help (and patience). That raises one last question which I have asked separately, but not received a satisfactory answer. If you feel like taking a look, my question regarding the editingStyle of a custom cell is posted here: http://stackoverflow.com/questions/3367637/setting-editingstyle-on-custom-cell-in-tablefooterview
Spindler