I have a UITableView which uses 2 NSFetchedResultsControllers. Each NSFetchedResultsController has only one section. However, the table has 4 sections. I populate the 4th section of the table with the results of one of the NSFetchedResultsControllers. Everything works fine so far. But if the user deletes the first cell on the first section, the NSFetchedResultsControllers are changed. The rows in the last section of the table might be deleted. When this method is called:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
NSLog(#"section: %d, row: %d", [newIndexPath section], [newIndexPath row]);
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
...
}
The section is always 0 because it's the section of the NSFetchedResultsControllers. Thus, the section doesn't match the correct one on the table view.
Is there a workaround? I would basically like to change the section of a NSFetchedResultsController to 3 instead of 0.
I found a workaround, but it would be nice to have a prettier solution.
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
if (newIndexPath != nil && controller == self.fetchedXController) {
newIndexPath = [NSIndexPath indexPathForRow:[newIndexPath row] inSection:3];
if ([tableView cellForRowAtIndexPath:newIndexPath] == nil) {
type = NSFetchedResultsChangeInsert;
}
}
if (indexPath != nil && controller == self.fetchedDomainsController) {
indexPath = [NSIndexPath indexPathForRow:[indexPath row] inSection:3];
}
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:newIndexPath] atIndexPath:newIndexPath];
break;
...
Related
I have a problem that Im sure many objectiveC developers would be able to solve pretty easily. Im developing an app which is a dynamic tableview with different groups that the user can create. The user can enter a number of groups with different totals. I am trying to sum the total of all the groups created.
The IBAction I am using to calculate the subtotal is
- (IBAction)Calculate:(id)sender {
//---------------
NSInteger Tempsum = [[_dataArray valueForKeyPath:#"#sum.name2"] integerValue];
_TotalAnimals.text = [[NSString alloc] initWithFormat:#"%.1ld", (long)Tempsum];
//--------------
}
I have created the following code in the .m file of my tableview and also created a NSArray property called;
#property NSArray *dataArray;
however I am unsure as to the correct code to use to be able to link the result ( #"#sum.name2" ) to the text value in the Tableviewcontroller ( _TotalAnimals.text)
Any help with this problem would be much appreciated. below is the full code for the .m file
#import "TSPViewController.h"
#import <CoreData/CoreData.h>
#import "TSPToDoCell.h"
#import "TSPAddToDoViewController.h"
#import "TSPUpdateToDoViewController.h"
int TempTotalAnimals = 0;
int TotalAnimals = 0;
int Tempsum = 0;
#interface TSPViewController () <NSFetchedResultsControllerDelegate>
#property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
#property (strong, nonatomic) NSIndexPath *selection;
#property NSArray *dataArray;
#end
#implementation TSPViewController
#pragma mark -
#pragma mark View Life Cycle
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize Fetch Request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:#"TSPItem"];
// Add Sort Descriptors
[fetchRequest setSortDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"createdAt" ascending:YES]]];
// Initialize Fetched Results Controller
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
// Configure Fetched Results Controller
[self.fetchedResultsController setDelegate:self];
// Perform Fetch
NSError *error = nil;
[self.fetchedResultsController performFetch:&error];
if (error) {
NSLog(#"Unable to perform fetch.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"addToDoViewController"]) {
// Obtain Reference to View Controller
UINavigationController *nc = (UINavigationController *)[segue destinationViewController];
TSPAddToDoViewController *vc = (TSPAddToDoViewController *)[nc topViewController];
// Configure View Controller
[vc setManagedObjectContext:self.managedObjectContext];
} else if ([segue.identifier isEqualToString:#"updateToDoViewController"]) {
// Obtain Reference to View Controller
TSPUpdateToDoViewController *vc = (TSPUpdateToDoViewController *)[segue destinationViewController];
// Configure View Controller
[vc setManagedObjectContext:self.managedObjectContext];
if (self.selection) {
// Fetch Record
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:self.selection];
if (record) {
[vc setRecord:record];
}
// Reset Selection
[self setSelection:nil];
}
}
}
#pragma mark -
#pragma mark Fetched Results Controller Delegate Methods
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
switch (type) {
case NSFetchedResultsChangeInsert: {
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeDelete: {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
case NSFetchedResultsChangeUpdate: {
[self configureCell:(TSPToDoCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
}
case NSFetchedResultsChangeMove: {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (void)configureCell:(TSPToDoCell *)cell atIndexPath:(NSIndexPath *)indexPath {
// Fetch Record
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
// Update Cell
[cell.nameLabel setText:[record valueForKey:#"name"]];
[cell.nameLabel2 setText:[record valueForKey:#"name2"]];
[cell.doneButton setSelected:[[record valueForKey:#"done"] boolValue]];
[cell setDidTapButtonBlock:^{
BOOL isDone = [[record valueForKey:#"done"] boolValue];
// Update Record
[record setValue:#(!isDone) forKey:#"done"];
}];
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObject *record = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (record) {
[self.fetchedResultsController.managedObjectContext deleteObject:record];
}
}
}
#pragma mark -
#pragma mark Table View Delegate Methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// Store Selection
[self setSelection:indexPath];
// Perform Segue
[self performSegueWithIdentifier:#"updateToDoViewController" sender:self];
}
// NSArray Below============
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSArray *sections = [self.fetchedResultsController sections];
id<NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TSPToDoCell *cell = (TSPToDoCell *)[tableView dequeueReusableCellWithIdentifier:#"ToDoCell" forIndexPath:indexPath];
// Configure Table View Cell
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (IBAction)Calculate:(id)sender {
//---------------
NSInteger Tempsum = [[_dataArray valueForKeyPath:#"#sum.name2"] integerValue];
_TotalAnimals.text = [[NSString alloc] initWithFormat:#"%.1ld", (long)Tempsum];
//--------------
}
/*
//---------------
NSInteger TempsumTotalAnimals = [[_charactersArray valueForKeyPath:#"#sum.passenger"] integerValue];
_TotalAnimals.text = [[NSString alloc] initWithFormat:#"%.1ld", (long)TempsumTotalAnimals];
//--------------
*/
#end
I am trying to show a table view with 2 sections. The first section will always have 1 row and the second section will have as many rows as data points. I am using Core Data and the following tableView:numberOfRowsInSection: method...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1;
} else {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
}
However, I am getting the following error:
Terminating app due to uncaught exception 'NSRangeException', reason:
'* -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
Any help will be appreciated. Thanks.
NEW -------------------------------------------------------------------------------
This is the current implementation of the relevant methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1;
} else {
NSUInteger frcSection = section - 1;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:frcSection];
return [sectionInfo numberOfObjects];
}
}
- (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];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
cell.textLabel.text = entityOne.name; //entityOne object passed from previous VC
} else {
entityTwo = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = entityTwo.name;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
NSUInteger frcSectionIndex = 0;
frcSectionIndex = sectionIndex + 1;
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:frcSectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:frcSectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
The reason is that the fetched results controller (FRC) has only one section (section #0), which you want to be displayed in the second section (section #1) of the table view.
This is possible, but you have to map between FRC section numbers and table view section numbers, e.g.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1;
} else {
NSUInteger frcSection = section - 1;
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:frcSection];
return [sectionInfo numberOfObjects];
}
}
The same mapping is necessary in cellForRowAtIndexPath.
In the FRC delegate methods didChangeObject, didChangeSection you have to add 1 to the section number before calling the table view methods (e.g. insertRowsAtIndexPaths).
ADDED: configureCell should look like this:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
cell.textLabel.text = entityOne.name; //entityOne object passed from previous VC
} else {
NSIndexPath *frcIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(indexPath.section - 1)];
entityTwo = [self.fetchedResultsController objectAtIndexPath:frcIndexPath];
cell.textLabel.text = entityTwo.name;
}
}
and didChangeObject like this:
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
UITableView *tableView = self.tableView;
NSIndexPath *tvIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(indexPath.section + 1)];
NSIndexPath *tvNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:(newIndexPath.section + 1)];
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:#[tvNewIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[tvIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:tvIndexPath] atIndexPath:tvIndexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[tvIndexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:#[tvNewIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
You probably get the idea:
When going from FRC index path to table view index path, add one to the section.
When going from table view index path to FRC index path, subtract one from the section.
I use 2 entities Person and Event that connected by relation 'events' one to many. in my first table view I have Person's names at the second I have their Events types. I can insert new event to specific person but the problem that the second table view don't update as well. only if I use navigator back to persons and after it go events again I see the changes.
for insert new event I use this method:
- (void) addEventControllerDidSave:(NSString *)typeData{
NSManagedObjectContext* context = [self managedObjectContext];
Event *newEvent = (Event *)[NSEntityDescription insertNewObjectForEntityForName:#"Event" inManagedObjectContext:context];
[newEvent setType:typeData];
[currentPerson addEventsObject:newEvent];
NSError *error;
if (![[self managedObjectContext] save:&error])
{
NSLog(#"Problem saving: %#", [error localizedDescription]);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
how I define the cell:
//create cell use relation
NSArray *eventsArray = [[[[self currentPerson] events] objectEnumerator]allObjects];
Event *newEvent = [eventsArray objectAtIndex:indexPath.row];
[[cell textLabel]setText: [newEvent type]];
update table methods:
- (void) controllerWillChangeContent:(NSFetchedResultsController *)controller{
[[self tableView] beginUpdates];
}
- (void) controllerDidChangeContent:(NSFetchedResultsController *)controller{
[[self tableView] endUpdates];
}
- (void) controller: (NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath{
UITableView *tableView = [self tableView];
switch (type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
{
NSArray *eventsArray = [[[[self currentPerson] events] objectEnumerator]allObjects];
Event *newEvent = [eventsArray objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[[cell textLabel]setText: [newEvent type]];
break;
}
case NSFetchedResultsChangeMove:
{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
default:
break;
}//switch
}
- (void) controller: (NSFetchedResultsController *) controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch (type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
withRowAnimation:UITableViewRowAnimationFade];
break;
default:
break;
}
}
I don't really know how update the tableView correct, I update my first tableView but the second have the problem that I describe early (maybe it because I use relation?)
you can also see my full project until now at the git (maybe it helps):
https://github.com/dennis87/eventCost
The EventsTableController has a fetchResultsController property and corresponding _fetchResultsController instance variable. But this FRC is always nil because it is never allocated + initialized in the EventsTableController.
The update methods controllerWillChangeContent, didChangeObject, controllerDidChangeContent are never called, because you have no FRC with a delegate.
As a workaround, you could add
[self.tableView reloadData];
in addEventControllerDidSave, then new entries will be visible immediately.
But the real solution would be to init and use a FRC, as you have already done in the NamesTableController. Then new events would be displayed automatically.
Creating a fetched results controller that fetches all events for the current person would look like this (this is your code from the NamesTableController, modified for use in the EventsTableController):
-(NSFetchedResultsController *) fetchResultsController{
if (_fetchResultsController != nil) {
return _fetchResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event"
inManagedObjectContext:[self managedObjectContext]];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"person = %#", self.currentPerson];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]initWithKey:#"type" ascending:YES];
NSArray *sortArray = [[NSArray alloc]initWithObjects:sort, nil];
[fetchRequest setSortDescriptors:sortArray];
_fetchResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:[self managedObjectContext]
sectionNameKeyPath:nil
cacheName:nil];
_fetchResultsController.delegate = self;
return _fetchResultsController;
}
I need to implement two different index paths on my tableview in cellForRow at index path... One with data from my fetched results controller and another for simple text.
What is the best way to go about this.
Im not exactly sure what your asking, but it sounds like you want a table with one section containing results from an NSFetchedResultController, and another section with data from some other source. This is easy to do. If your data can doesn't need to be in multiple sections then it's very easy, just get data from fetchedResultController for section 0, and something else for section 1.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if(section == 0){
id sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}else{
return self.someArray.Count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexpath.section == 0){
static NSString *CellIdentifier = #"data cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSObject *foo = [self.fetchedResultController objectAtIndexPath:indexPath];
//configure cell
return cell;
}else{
static NSString *CellIdentifier = #"some other cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//configure cell
return cell;
}
}
I have created one application with core data but is unable to display the data in table view. I am writing the code below which I have written for fetching the results:
#import "RootViewController.h"
#import "DetailViewController.h"
#import "AddViewController.h"
#import "EmployeeDetailsAppDelegate.h"
/*
This template does not ensure user interface consistency during editing operations in the table view. You must implement appropriate methods to provide the user experience you require.
*/
#interface RootViewController ()
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
#end
#implementation RootViewController
#synthesize detailViewController, fetchedResultsController, managedObjectContext;
#synthesize array;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
// EmployeeDetailsAppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
self.title = #"Employee Name";
self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSMutableArray *tempArr = [[NSMutableArray alloc]initWithCapacity:0];
if (self.fetchedResultsController) {
tempArr = self.fetchedResultsController.fetchedObjects;
}
self.array = tempArr;
[super viewDidLoad];
[tempArr release];
}
- (void)viewWillAppear:(BOOL)animated {
[self.tableView reloadData];
[super viewWillAppear:animated];
}
/*
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
}
*/
/*
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
}
*/
/*
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
}
*/
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Ensure that the view controller supports rotation and that the split view can therefore show in both portrait and landscape.
return YES;
}
#pragma mark -
#pragma mark Add a new object
- (void)insertNewObject:(id)sender {
AddViewController *add = [[AddViewController alloc]initWithNibName:#"AddViewController" bundle:nil];
self.modalPresentationStyle = UIModalPresentationFormSheet;
add.wantsFullScreenLayout = NO;
[self presentModalViewController:add animated:YES];
[add release];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if([self.array count])
{
return [array count];
}
return 1;
}
- (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];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
-(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath{
AddViewController *detail = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = detail.empName.text;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the managed object.
NSManagedObject *objectToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
if (self.detailViewController.detailItem == objectToDelete) {
self.detailViewController.detailItem = nil;
}
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
[context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error;
if (![context save:&error]) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button.
*/
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// The table view should not be re-orderable.
return NO;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Set the detail item in the detail view controller.
detailViewController = [[DetailViewController alloc]initWithStyle:UITableViewStylePlain];
AddViewController *selectedName = (AddViewController *)[[self fetchedResultsController]objectAtIndexPath:indexPath];
detailViewController.detail = selectedName;
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
}
#pragma mark -
#pragma mark Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController {
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
/*
Set up the fetched results controller.
*/
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Details" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"EmployeeName" ascending:NO];
NSSortDescriptor *idDescriptor = [[NSSortDescriptor alloc] initWithKey:#"EmployeeID" ascending:NO];
NSMutableArray *sortDescriptors = [[NSMutableArray alloc] initWithObjects:nameDescriptor, idDescriptor, nil];
self.array = sortDescriptors;
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[nameDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
#pragma mark -
#pragma mark Fetched results controller delegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
self.fetchedResultsController = nil;
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
self.array = nil;
}
- (void)dealloc {
[array release];
[detailViewController release];
[fetchedResultsController release];
[managedObjectContext release];
[super dealloc];
}
#end
The problem is I am getting a warning with the following line:
tempArr = self.fetchedResultsController.fetchedObjects;
The warning is: "/Users/satyam/Desktop/EmployeeDetails/Classes/RootViewController.m:43:0 /Users/satyam/Desktop/EmployeeDetails/Classes/RootViewController.m:43: warning: incompatible Objective-C types assigning 'struct NSArray *', expected 'struct NSMutableArray *'"
Just read the warning word for word.
you try to assign an NSArray to a variable that expects a NSMutableArray.
self.fetchedResultsController.fetchedObjects is an NSArray
tempArr is an NSMutableArray
but if you use the NSFetchedResultsController you don't need your own data array at all.
The NSFetchedResultsController handles all the data for you.
so maybe you want to replace
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if([self.array count])
{
return [array count];
}
return 1;
}
with
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
your own datasource is invalid anyway. because you assign the sortDescriptor used for the NSFetchedResultsController to it.
NSMutableArray *sortDescriptors = [[NSMutableArray alloc] initWithObjects:nameDescriptor, idDescriptor, nil];
self.array = sortDescriptors;
I don't know what you wanted to achieve by doing this.