I'm so close... but must be missing something.
My local DB has a list of golf courses with various details including among other things, the State, first letter of the state's name (A, C, D, ) etc... The NSFetchedResultsController is grabbing a list of courses and is (or was) working great showing all the states that that had a course I have records for.
At any rate... the section heads seem to be working... but my states are now being duplicated for the number of courses that each given state has.
Code and screen shot below. What am I missing?!? It has to be something so obvious.
-(NSFetchedResultsController *)fetchedResultsController {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Courses"
inManagedObjectContext:_managedObjectContext];
request.entity = entity;
request.sortDescriptors = [NSArray arrayWithObject:
[NSSortDescriptor
sortDescriptorWithKey:#"locState"
ascending:YES
selector:#selector(caseInsensitiveCompare:)]];
request.returnsDistinctResults = YES;
request.fetchBatchSize = 20;
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"locStateSectionHead"
cacheName:nil];
frc.delegate = self;
NSError *error = nil;
if (![frc performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
self.fetchedResultsController = frc;
return _fetchedResultsController;
}
And then the rest of the tableView setup:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
if ([[_fetchedResultsController sections] count] > 0) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[_fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
} else {
return 0;
}
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [self.fetchedResultsController sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"stateCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
// Configure the cell...
Courses *course_info = [_fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = course_info.locState;
return cell;
}
Assuming you want your table view to display the states then your NSFetchedResultsController should be fetching the State not the Course. You are getting duplication because you are sorting from courses and you have multiple courses in a state.
Configure your NSFetchedResultsController to load the State entity then your table view will display properly. From there when a user selects a state you can use the relationship from State to Course to display your next scene.
You simply have it backwards :)
Related
this is my UITableViewDateSource code:
#import "TableViewDataSource.h"
#implementation TableViewDataSource
#synthesize tableView;
#synthesize tableCell;
#synthesize LHfetchedResultsController;
#synthesize numberOfRows;
#synthesize dataTemp;
#synthesize paused;
-(id)initWithTableView:(UITableView *) tableView
{
self = [super init];
if (self) {
self.tableView = tableView;
self.tableView.dataSource = self;
self.tableView.delegate = self;
}
return self;
}
-(void)dateSourceWithChineseNewestVideosFetchedResultsController
{
self.tableCell = [TableCellModel getCellOfFreeChampionsList];
[self setChineseNewestVideosLHFetchedResultsController];
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
[self configCellData:indexPath cell:self.tableCell];
// Configure the cell with data from the managed object.
return self.tableCell;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if ([[self.LHfetchedResultsController sections] count] > 0) {
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.LHfetchedResultsController sections] objectAtIndex:section];
self.numberOfRows = [sectionInfo numberOfObjects];
// return self.numberOfRows;
return 1;
} else
return 0;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.LHfetchedResultsController sections] count];
}
-(void)configCellData:(NSIndexPath *)indexPath cell:(id)cell
{
NSLog(#"DataSourceTempCount is %lu",(unsigned long)self.dataTemp.count);
// NSInteger indexNum = indexPath.row;
// ChineseNewestVideos * Video = [self.dataTemp objectAtIndex:indexNum];
ChineseNewestVideos * Video = [self.LHfetchedResultsController objectAtIndexPath:indexPath];
UITextView * textView = [self.tableCell viewWithTag:11];
textView.text = Video.title;
UILabel * la = [self.tableCell viewWithTag:12];
la.text = [NSString stringWithFormat:#"Data Count is %lu",(unsigned long)self.numberOfRows];
if (indexPath.row %2 == 0) {
self.tableCell.backgroundColor = [UIColor greenColor];
}else
{
self.tableCell.backgroundColor = [UIColor brownColor];
}
}
-(void)setLHFetchedResultsController:(NSFetchedResultsController *)fetchedResultsController
{
self.LHfetchedResultsController = fetchedResultsController;
self.LHfetchedResultsController.delegate = self;
NSError * err;
if (![self.LHfetchedResultsController performFetch:&err]) {
//启动
NSLog(#"Unresolved error : %#, %#",err,[err userInfo]);
exit(-1);
}
}
-(void)setChineseNewestVideosLHFetchedResultsController
{
NSManagedObjectContext * context = ((AppDelegate*)[[UIApplication sharedApplication] delegate]).persistentContainer.viewContext;
NSFetchRequest * request = [ChineseNewestVideos fetchRequest];
NSEntityDescription * testEntity = [NSEntityDescription entityForName:#"ChineseNewestVideos" inManagedObjectContext:context];
request.entity = testEntity;
NSSortDescriptor * sort = [[NSSortDescriptor alloc] initWithKey:#"createdate" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sort]];
// [request setFetchBatchSize:20];
NSFetchedResultsController * ChineseNewestVideosLHFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];
[self setLHFetchedResultsController:ChineseNewestVideosLHFetchedResultsController];
}
TableView is a property of ViewController
data has no problem, but display of table has problem
start from -(void)setChineseNewestVideosLHFetchedResultsController
when i set different return of -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section, display is different
like this:
when return 1
when return one
when return 2
when return two
when return 3
[when return three][3]
when return 4 or more
[when return four or more][4]
display always like this,and when i drop up, the textview will suddenly disappear
who know what happen?
You need to init every cell in cellForRowAtIndexPath thats why there is only one tableview cell. You need to "create" cell for each row. So put that method in the
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
TableCellModel *cell = [TableCellModel getCellOfFreeChampionsList];
[self configCellData:indexPath cell:self.tableCell];
return cell;
}
I recently migrated a project from a self-managed object using a config singleton to use the NSManaged Object Context with NSFetchedResultController. What I'm trying to do is fill a TableView with sections that are based on month however the user can select a cell and change the month. when that happens it causes the following exception thrown and the cells become unable to change or edit
[error] error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to insert row 0 into section 1, but there are only 0 sections after the update with userInfo (null)
CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. attempt to insert row 0 into section 1, but there are only 0 sections after the update with userInfo (null)
Here is the main view controller fetch request:
- (NSFetchedResultsController<Budget *> *)fetchedResultsController
{
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
LogDebug(#"STARTED");
NSFetchRequest<Budget *> *fetchRequest = Budget.fetchRequest;
[fetchRequest setFetchBatchSize:20];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor]];
NSFetchedResultsController<Budget *> *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"monthSection" cacheName:nil];
aFetchedResultsController.delegate = self;
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
LogError(#"Unresolved error %#, %#", error, error.userInfo);
abort();
}
_fetchedResultsController = aFetchedResultsController;
return _fetchedResultsController;
}
Then for the Tableview Sections Data Source:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
NSInteger count = [sectionInfo numberOfObjects];
LogDebug(#"Number of Rows: %ld in Section %ld",(long)count, (long)section);
return [sectionInfo numberOfObjects];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
NSInteger count = [[self.fetchedResultsController sections] count];
LogDebug(#"Sections: %ld",(long)count);
return [[self.fetchedResultsController sections] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
LogDebug(#"STARTED");
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController.sections objectAtIndex:section];
return [sectionInfo name];
}
I have also tried subclassing the data object model and added this to setting the month section:
- (NSString *)monthSection {
#synchronized (self.startTime) {
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:#"MMMM"];
NSString *sectionTitle = [formatter stringFromDate:self.startTime];
return sectionTitle;
}
}
Now when the user selects a table view cell I send the Budget Object to the DetailViewController by sending it the following way:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:#"showDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
Budget *object = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
DetailViewController *controller = (DetailViewController *)[[segue destinationViewController] topViewController];
[controller setDetailItem:object];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
}
}
Then in DetailViewController i just use the setter on the startTime:
[startDatePicker setDate:detailItem.startTime];
[[AppDelegate instance] saveContext];
[self totalUpFields];
But once the user changes the Month date to something other than what was initially created it thrown that above exception.
I'm very new to the NSManaged Object structure and I've always used a managed config singleton.
For the changed object content here are the methods:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView endUpdates];
}
- (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:
return;
}
}
- (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] withBudget:anObject];
break;
case NSFetchedResultsChangeMove:
[tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
break;
}
}
Thanks for the help, let me know if i need to add additional details.
I believe this is an issue when moving the last row out of a section. You can resolve it by changing your case NSFetchedResultsChangeMove: to the following:
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
It looks like this happens because the sections are modified before the rows, so the index paths shift and the expected source/destination may no longer be valid.
It's also important to first sort by your sectionNameKeyPath:
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"monthSection" ascending:NO];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"startTime" ascending:NO];
[fetchRequest setSortDescriptors:#[sortDescriptor1, sortDescriptor2]];
You will need to add monthSection as a property on your Core Data model and set it when inserting/updating, instead of the dynamic method you're currently using in your subclass (otherwise you'll get an exception when performing the fetch).
I been working in a project where i use core data to store some information, I have a UITableViewController with a UISegmentedControl and two NSFetchedResultsController because i need to fetched different data when i switch the segmented control.
When I first open the table view i can see the data corresponding to the first index of the segmented controller, but when i switch to the second index the data isn´t show and when i go back to the first index of the segmented control the data isn't show too.
This is the code
#import "TurnHistoryVC.h"
#import "AppDelegate.h"
#import "Turn.h"
#import "TurnInfoVC.h"
#import "Favorite.h"
#interface TurnHistoryVC ()
#property (nonatomic, retain) NSFetchedResultsController *currentFRC;
#property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
#property (nonatomic, strong) NSFetchedResultsController *favoritesResultsController;
#end
#implementation TurnHistoryVC
#synthesize fetchedResultsController = _fetchedResultsController;
#synthesize favoritesResultsController = _favoritesResultsController;
- (void)viewWillAppear:(BOOL)animated{
NSInteger *turn_id = [[NSUserDefaults standardUserDefaults] integerForKey: #"turn_noti_id"];
if (turn_id != 0) {
[self performSegueWithIdentifier:#"myTurn" sender:self];
} else {
id delegate = [[UIApplication sharedApplication] delegate];
self.managedObjectContext = [delegate managedObjectContext];
NSError *error;
self.currentFRC = self.fetchedResultsController;
if (![[self currentFRC] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
}
[self.tableView reloadData];
[super viewWillAppear:animated];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Atrás" style:UIBarButtonItemStylePlain target:nil action:nil];
[self.tableView reloadData];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.currentFRC sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.currentFRC sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Turn *turn = [self.currentFRC objectAtIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:#"Turno no. %#", turn.turn_number];
cell.detailTextLabel.text = turn.companie_info;
cell.tag = [turn.turn_id intValue];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
// Display the authors' names as section headings.
// return #"";
return [[[self.currentFRC sections] objectAtIndex:section] name];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"cell id: %i", selectedCell.tag);
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
self.editButtonItem.title = #"Listo";
// [tableView ]
// Delete the managed object.
NSManagedObjectContext *context = [self.currentFRC managedObjectContext];
[context deleteObject:[self.currentFRC objectAtIndexPath:indexPath]];
NSError *error;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
// abort();
}
}
}
//Metodo para cambiar el texto del boton para borrar favoritos
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
return #"Elimiar";
}
-(void)toggleEdit{
[self.tableView setEditing:!self.tableView.editing animated:YES];
if (self.tableView.editing){
[self.navigationItem.leftBarButtonItem setTitle:#"Listo"];
}else{
[self.navigationItem.leftBarButtonItem setTitle:#"Editar"];
}
}
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([sender isKindOfClass:[UITableViewCell class]]){
if ([segue.destinationViewController isKindOfClass:[TurnInfoVC class]]){
TurnInfoVC *turnInfoVC = segue.destinationViewController;
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
UITableViewCell *selectedCell = [self.tableView cellForRowAtIndexPath:indexPath];
turnInfoVC.turn_id = selectedCell.tag;
}
}
}
#pragma mark - Cambio de turnos pendientes a finalizados
- (IBAction)btnTurnTypes:(UISegmentedControl *)sender {
NSError *error;
NSLog(#"cambie de tab 0");
switch (sender.selectedSegmentIndex) {
case 0:
{
self.currentFRC = self.fetchedResultsController;
[self.tableView reloadData];
}
break;
case 1:
{
self.currentFRC = self.favoritesResultsController;
[self.tableView reloadData];
NSLog(#"cambie de tab 1");
}
break;
default:
break;
}
[self.tableView reloadData];
}
#pragma mark - Fetched results controller
/*
Returns the fetched results controller. Creates and configures the controller if necessary.
*/
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
}
NSLog(#"turnos pendientes");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Turn"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"turn_confirmation==0 AND turn_status==1"];
fetchRequest.predicate = predicate;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Turn" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *categoryDescriptor = [[NSSortDescriptor alloc] initWithKey:#"companie_info" ascending:YES];
NSSortDescriptor *companyDescriptor = [[NSSortDescriptor alloc] initWithKey:#"turn_number" ascending:YES];
NSArray *sortDescriptors = #[categoryDescriptor, companyDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"companie_info" cacheName:#"Root"];
_fetchedResultsController.delegate = self;
self.currentFRC = _fetchedResultsController;
return _fetchedResultsController;
}
- (NSFetchedResultsController *)favoritesResultsController {
if (_favoritesResultsController != nil) {
}
NSLog(#"turnos finalizados");
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"Turn"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"turn_confirmation==0 AND turn_status==0"];
fetchRequest.predicate = predicate;
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Turn" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
// Create the sort descriptors array.
NSSortDescriptor *categoryDescriptor = [[NSSortDescriptor alloc] initWithKey:#"companie_info" ascending:YES];
NSSortDescriptor *companyDescriptor = [[NSSortDescriptor alloc] initWithKey:#"turn_number" ascending:YES];
NSArray *sortDescriptors = #[categoryDescriptor, companyDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
// Create and initialize the fetch results controller.
_favoritesResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"companie_info" cacheName:#"Root"];
_favoritesResultsController.delegate = self;
self.currentFRC = _favoritesResultsController;
return _favoritesResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
NSLog(#"actualizando fuera del if");
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
if (controller == self.currentFRC) {
NSLog(#"actualizando dentro del if");
[self.tableView beginUpdates];
}
}
- (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:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (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:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
#end
I already try this solution, but i can't get it to work, thanks
In the following code reordering of rows in UITableView only works in the first section.
After reordering rows in sections other than the first ,when the view reappears the rows go back to their original order
Ive googled to no avail.
Can anyone please help me get reordering working in all sections?
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;
userDrivenDataModelChange = YES;
NSMutableArray *things = [[__fetchedResultsController fetchedObjects] mutableCopy];
// Grab the item we're moving.
NSManagedObject *thing = [[self fetchedResultsController] objectAtIndexPath:sourceIndexPath];
// Remove the object we're moving from the array.
[things removeObject:thing];
// Now re-insert it at the destination.
[things insertObject:thing atIndex:[destinationIndexPath row]];
// All of the objects are now in their correct order. Update each
// object's displayOrder field by iterating through the array.
int i = 0;
for (NSManagedObject *mo in things)
{
[mo setValue:[NSNumber numberWithInt:i++] forKey:#"displayOrder1"];
}
[things release], things = nil;
[__managedObjectContext save:nil];
userDrivenDataModelChange = NO;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (NSFetchedResultsController *)fetchedResultsController
{
if (__fetchedResultsController != nil)
{
return __fetchedResultsController;
}
// Edit the entity name as appropriate.
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:#"Client" inManagedObjectContext:context]];
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"displayOrder1" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"area" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor2 ,sortDescriptor ,nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptor2 release];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"area" cacheName:#"Root"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptors release];
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
sectionInsertCount = 0;
if (userDrivenDataModelChange)return; {
[self.tableView beginUpdates];
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
if (userDrivenDataModelChange)return;{
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
{
if (userDrivenDataModelChange)return; {
UITableView *aTableView = self.tableView;
switch(type)
{
case NSFetchedResultsChangeInsert:
[aTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[aTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[aTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[aTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
if (userDrivenDataModelChange) return;
[self.tableView endUpdates];
}
- (void)saveContext {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
}
#end
Since your things array is a mutable copy of the fetched results controller's fetchedObjects, none of the manipulations you do to things has any result on fetchedObjects.
Only the manual change shows up because all that change occurs in the fetched results controller's didChange... methods.
Edit:
Your problem is here:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"displayOrder1" ascending:YES];
NSSortDescriptor *sortDescriptor2 = [[NSSortDescriptor alloc] initWithKey:#"area" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor2 ,sortDescriptor ,nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptor release];
[sortDescriptor2 release];
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"area" cacheName:#"Root"];
From the NSFetchedResultsController docs:
sectionNameKeyPath ... If this key path is not the same as that
specified by the first sort descriptor in fetchRequest, they must
generate the same relative orderings. For example, the first sort
descriptor in fetchRequest might specify the key for a persistent
property; sectionNameKeyPath might specify a key for a transient
property derived from the persistent property.
Your primary sort key is displayOrder but your sectionNameKeyPath is area and I doubt they produce the same sort order.
It is usually bad practice to put an interface function such as displayOrder into the data model. What happens if you have multiple tables, all with different orders? Unless the ordering is arbitrary and something that the app needs to persist, don't create the attribute.
Also, in two places in the code, you have this construct:
if (userDrivenDataModelChange)return;{
//...
}
While syntactically legal, this is just an bug waiting to happen. If the statement is true, the method returns immediately. If false, the block executes. That is an ugly, ugly construction that is way to easy to misread. Plus, you are issuing a void return. The compiler will warn you about this and you should pay attention to it.
Re: "an explanation why it only works in the first section and not in the others"
Scenario:
Section 1
objectA at indexPath.section = 0, indexPath.row = 0
objectB at indexPath.section = 0, indexPath.row = 1
objectC at indexPath.section = 0, indexPath.row = 2
Section 2
objectAA at indexPath.section = 1, indexPath.row = 0
objectBB at indexPath.section = 1, indexPath.row = 1
objectCC at indexPath.section = 1, indexPath.row = 2
When you copy objects into mutable array, your are "loosing" indexPath.section and your array looks like:
objectA at index 0
objectB at index 1
objectC at index 2
objectAA at index 3
objectBB at index 4
objectCC at index 5
When you remove and insert an object in 1st section you insert it at indexPath.row from 0 to 2 into your array indexes from 0 to 2, which is correct so it works
When you remove and insert an object in 2nd section you still insert it at indexPath.row from 0 to 2 (but indexPath.section 1 already) but your array does not have a section. so it is again inserted at indexes from 0 to 2, instead of 3 to 5, so it does not sort correctly
I have 2 tables in coredata sqlite Painter and Picture. With relationship one-to-many.
In table "picture" I have string attribute pictureName. I store pictures(153) on a disk
This code I add imageViews to cells:
- (void)viewDidLoad
{
[super viewDidLoad];
NSError *error = nil;
if (![[self fetchedResultsController] performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
self.tableView.transform = CGAffineTransformMakeRotation( -M_PI/2 );
self.tableView.showsHorizontalScrollIndicator = NO;
self.tableView.showsVerticalScrollIndicator = NO;
[self.tableView setFrame:CGRectMake(0, 156, 1024, 449)];
}
- (void)viewDidUnload
{
[self setTableView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#pragma mark - UITableView Delegate Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [[fetchedResultsController fetchedObjects] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Painter *painter = [[fetchedResultsController fetchedObjects] objectAtIndex:section];
//NSLog(#"%i", [painter.pictures count]);
return [painter.pictures count];
//return 152;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Painter *painter = [[fetchedResultsController fetchedObjects] objectAtIndex:indexPath.section];
Picture *picture = [[painter.pictures allObjects] objectAtIndex:indexPath.row];
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[NSString stringWithFormat:#"%#s.jpg", picture.imgName]]];
NSLog(#"add image %#s.jpg to sector:%i row:%i", picture.imgName, indexPath.section, indexPath.row);
imageView.transform = CGAffineTransformMakeRotation( M_PI/2 );
[cell addSubview:imageView];
[imageView release];
}
- (UITableViewCell *)tableView:(UITableView *)tableViewCurrent cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableViewCurrent dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 300.0;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
}
#pragma mark - Fetched results controller
- (NSFetchedResultsController *)fetchedResultsController
{
if (fetchedResultsController != nil) {
return fetchedResultsController;
}
// Create the fetch request for the entity.
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Painter" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[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:#"Main"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;
[aFetchedResultsController release];
[fetchRequest release];
[sortDescriptor release];
[sortDescriptors release];
return fetchedResultsController;
}
`
And I have problem: a lot of photo in every cell
http://ge.tt/9QX5lc4?c
(select view button)
Why?
Cells are being reused. Every time you are configuring a cell, you are adding an image view as a subview. Over time these accumulate and are providing the effect you are seeing. You should check if an image view already exists and assign an image to it or clean up the cell first and then set it up with an image.
The following code is what is causing the problem for you:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableViewCurrent dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
You're basically caching the cell the first time it's created and re-using it on subsequent calls to tableView:cellForRowAtIndexPath: If you don't want this to be cached then you'll need to create a new cell every time.
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// Configure the cell.
[self configureCell:cell atIndexPath:indexPath];
return cell;
This should fix your issue. There are more elaborate ways of handling cells such as caching it and manipulating its subviews directly instead of recreating it every time. Something to keep in mind as you continue to work on your code.