NSFetchedResultsController: Multiple FRCs, Delegate Error when Updating - ios

Objective: Using FRC, sort Section's by startDate, an NSDate attribute, but want Today's date Section to appear before Upcoming dates Section.
I followed Apple's code using a transient property sectionIdentifier. Apple's sample code. and started with this project first: OneFRC
I soon realized that this may not be possible with just one FRC (I could be wrong).
Next, I decided to take a stab at this with 3 FRCs: ThreeFRC.
TableView sections now appears in the Order that I want:
Section 0: Today
Section 1: Upcoming
Section 2: Past
However, adding data triggers FRC delegates, and I get the following error:
CoreData: error: Serious application error. An exception was caught from the
delegate of NSFetchedResultsController during a call to
-controllerDidChangeContent:. Invalid update: invalid number of rows in section 0.
The number of rows contained in an existing section after the update (4) must be
equal to the number of rows contained in that section before the update (3), plus
or minus the number of rows inserted or deleted from that section (0 inserted,
0 deleted) and plus or minus the number of rows moved into or out of that section
(0 moved in, 0 moved out). with userInfo (null)
Again, I would love to be able to accomplish my objective with 1 FRC, but I can't seem to figure out how.
I have been trying to resolve this for 4 days now! If this issue doesn't get resolved on SO, I think I may reach out to Apple for Developer support. And in the event that I do, I'll post the resolution here so others can benefit.
Projects are available on Github:
One FRC
Three FRC
EDIT
Thanks to #blazejmar, I was able get rid of the rows error. However, now I get an error when I attempt to add sections.
2014-11-03 16:39:46.852 FRC[64305:60b] CoreData: error: Serious application error.
An exception was caught from the delegate of NSFetchedResultsController during a
call to -controllerDidChangeContent:. Invalid update: invalid number of sections.
The number of sections contained in the table view after the update (2) must be
equal to the number of sections contained in the table view before the update (1),
plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
with userInfo (null)
Steps to reproduce the error in Three FRC:
1. Launch App ->
2. Tap Generate Data ->
3. Tap View in FRC ->
4. Tap back to the RootVC ->
5. Change the system date to a month from Today ->
6. Tap View in FRC and only one section `Past` should appear. ->
7. Tap `Add Data`.
8. The error should appear in the log.

In your ThreeFRC project there are some issues:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
self.numberOfSectionsInTV = 0;
[self fetchData];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView reloadData];
[self.tableView endUpdates];
}
You shouldn't use fetchData inside FRC delegate. Methods are called in proper order (before, during and after update) so inside callbacks you have consistent state of context. Also it's not the best idea to use reloadData before endUpdates(it's applying all changes you provided earlier) and reloadData is erasing everything and building it from scratch. This is most likely causing the crash.
Other thing I've spotted that may be buggy is handling of updates. If you have 3 separate FRC without sections you won't get section update callback in FRC delegate. But if some objects appear in one of the FRC's then you should detect that and manually insert them.
Using just reloadData in controllerDidChangeContent would be enough, but this isn't the best solution, as you won't get any animations. The proper way would be to handle all the cases: deleting all objects from one of FRCs (and then deleting section manually from TableView), inserting first object into FRC (then you should create new section at proper indexPath).

i looked at your ThreeFRC project and noticed that in - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView you check which FRCs contain objects and that would determine the number of sections. this makes logical sense, but really confuses the FRC delegate when adding/deleting "sections" (or, when your other FRCs suddenly have objects). For example, you only have a Past section (1 section), but then the data changes such that you now also have a Today section. Since sectionPastFRC or the other FRCs didn't have any section changes, there are no calls to - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type, and though you should have 2 sections now, there were no calls to add, delete, or move sections. you'd have to update the sections manually somehow, which may be a pain.
here's the workaround i suggest: since you will ALWAYS have at most one section for each FRC, you should just return 3 in - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView. This is so there will no longer be any problem in adding/deleting a section because they were all already there. Anyway, if, for example, the Today section has no objects, just return 0 in - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section. Just make sure that in - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section, if fetchedObjects==0, return nil, so that it also won't display the section header if that section has no objects. And in your FRC delegate didChangeObject, just always adjust the indexPath and newIndexPath before performing changes on the tableView.
note that this workaround will only work if you already know the maximum number of sections that the FRCs (except the last FRC) will need. it is NOT a solution for all implementations of multiple FRCs in a single table view. i've actually used this solution in a project where i had 2 FRCs for one tableView, but the first FRC would only always take up 1 section, while the second FRC could have any number of sections. i always just had to adjust the sections +1 for changes in the second FRC.
i've actually tried applying the changes i mentioned above into your code, and haven't been getting errors. here are the parts i changed in the UITableViewDataSource:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rows = 0;
switch (section) {
case 0:
{
rows = [[self.sectionTodayFRC fetchedObjects]count];
break;
}
case 1:
{
rows = [[self.sectionUpcomingFRC fetchedObjects]count];
break;
}
case 2:
{
rows = [[self.sectionPastFRC fetchedObjects]count];
break;
}
}
NSLog(#"Section Number: %i Number Of Rows: %i", section,rows);
return rows;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *header;
switch (section) {
case 0:
{
if ([[self.sectionTodayFRC fetchedObjects]count] >0)
{
header = #"Today";
}
break;
}
case 1:
{
if ([[self.sectionUpcomingFRC fetchedObjects]count] >0)
{
header = #"Upcoming";
}
break;
}
case 2:
{
if ([[self.sectionPastFRC fetchedObjects]count] >0)
{
header = #"Past";
}
break;
}
}
return header;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
Meeting *meeting;
switch (indexPath.section) {
case 0:
if ([[self.sectionTodayFRC fetchedObjects]count] > 0)
{
meeting = [[self.sectionTodayFRC fetchedObjects] objectAtIndex:indexPath.row];
}
break;
case 1:
if ([[self.sectionUpcomingFRC fetchedObjects]count] > 0)
{
meeting = [[self.sectionUpcomingFRC fetchedObjects] objectAtIndex:indexPath.row];
}
break;
case 2:
if ([[self.sectionPastFRC fetchedObjects]count] > 0)
{
meeting = [[self.sectionPastFRC fetchedObjects] objectAtIndex:indexPath.row];
}
break;
}
cell.textLabel.text = meeting.title;
return cell;
}
and for the NSFetchedResultsControllerDelegate:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
NSLog(#"Inside didChangeObject:");
NSIndexPath *modifiedIndexPath;
NSIndexPath *modifiedNewIndexPath;
if (controller == self.sectionTodayFRC)
{
modifiedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:0];
modifiedNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:0];
}
else if (controller == self.sectionUpcomingFRC)
{
modifiedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:1];
modifiedNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:1];
}
else if (controller == self.sectionPastFRC)
{
modifiedIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:2];
modifiedNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:2];
}
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:#[modifiedNewIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeDelete:
NSLog(#"frcChangeDelete");
[self.tableView deleteRowsAtIndexPaths:#[modifiedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeUpdate:
NSLog(#"frcChangeUpdate");
[self.tableView reloadRowsAtIndexPaths:#[modifiedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
case NSFetchedResultsChangeMove:
NSLog(#"frcChangeDelete");
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:modifiedIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:modifiedNewIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
break;
}
}
i hope this helps someone!

Related

How to expand section in tableview in iOS?

I have a custom header for each section in tableView, and the headerView has a button. On click of that button, I'm trying to expand the section by changing the number of rows for that section. It works fine if I call reloadData, but it crashes when I try to use reloadSections/ insert/delete sections.
HEre is my number of rows method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (highlighHeaderClicked) {
return [_expandCountArray[section]integerValue]; // 9,9,9
}
return [_collapseCountArray[section]integerValue]; //2,2,2
}
So by default tableView shows 2 rows, when button is clicked, I want to show 9 rows.
and the button action method:
-(IBAction)highlightHeaderClicked:(id)sender{
highlighHeaderClicked = !highlighHeaderClicked;
NSIndexPath *indexPath = [self getIndexPathForView:sender];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:indexPath.length-1];
[self.tableView beginUpdates];
[self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView insertSections:indexSet withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
}
By doing this I got the excpetion:
Invalid update: invalid number of rows in section 2. The number of rows contained in an existing section after the update (9) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
I also tried removing the object from datasource when the button action method gets called.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_tempRowCountArray[section]integerValue]; //2,2,2
}
-(IBAction)highlightHeaderClicked:(id)sender{
highlighHeaderClicked = !highlighHeaderClicked;
NSIndexPath *indexPath = [self getIndexPathForView:sender];
NSIndexSet *indexSet = [NSIndexSet indexSetWithIndex:indexPath.length-1];
NSInteger rowCount = _mainDataSet.dashboardArray.count;
if (rowCount > 2) {
if (highlighHeaderClicked) {
[_tempRowCountArray replaceObjectAtIndex:indexPath.length withObject:#(rowCount)];
}else{
[_tempRowCountArray replaceObjectAtIndex:indexPath.length withObject:#2];
}
[self.tableView beginUpdates];
[self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView insertSections:indexSet withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
}
}
What am I missing here ? I think I am passing the right array count each time.
The basic problem you have is that you are trying to insert and remove whole sections. That means the number of sections would have to change.
Instead, you have to remove and insert rows in sections.
Also, you have to specify the exact difference between the previous state and the new state.
For example:
NSArray *rowsToBeAdded = ...
NSArray *rowsToBeRemoved = ...
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:rowsToBeAdded withRowAnimation: UITableViewRowAnimationBottom];
[self.tableView deleteRowsAtIndexPaths:rowsToBeRemoved withRowAnimation: UITableViewRowAnimationBottom];
[self.table endUpdates];
That also means you have to be very careful about your logic and keep track of the sections that are expanded.
You have to return the correct number of items for collapses and expanded sections:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
BOOL expanded = ...;
if (!expanded) {
return 0;
}
return ...
}
One other solution for this problem is to keep the rows always there and avoid inserts & deletes completely. You can return zero height for all hidden rows.
To update the height of all rows, you then simply call:
[self.tableView beginUpdates];
[self.tableView endUpdates];

UITableView and NSFetchedResultsController row deletion error [duplicate]

Deleting a row from a UITableView fed by an NSFetchedResultsController causes my app to crash.
Error is:
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-2903.23/UITableView.m:1330
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
I want only swipe-to-delete. My deletion code goes like this:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.tableView beginUpdates];
SequenceData *sd = [self.fetchedResultsController objectAtIndexPath:indexPath];
[self.managedObjectContext deleteObject:sd];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
My NSFetchedResultsController is set up just like in Ray Wanderlich's tutorial (http://www.raywenderlich.com/999/core-data-tutorial-for-ios-how-to-use-nsfetchedresultscontroller)
Number of rows is determined by:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id sectionObjects = [[self.fetchedResultsController sections] objectAtIndex:section];
NSInteger nbObjects = [sectionObjects numberOfObjects];
return nbObjects;
}
It looks like the fetch is not updating (number of rows does not vary). Do I need to fetch myself (isn't this taking care of by the fetch controller)?
There is obviously something basic I am missing here. Don't hesitate to suggest basic answers.
I first implemented this with the controller: didChangeObject: ... method. Error was the same, but some details differed.
Edit
I believe my problem is fixed. Both answers (From CX and Martin) helped me find it. Martin got the answer because of the explanations that helped me understand a little bit better...
Don't call
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if you use a fetched results controller. Only delete the object with
[self.managedObjectContext deleteObject:sd];
The fetched results controller delegate method didChangeObject: is then called automatically,
and that calls deleteRowsAtIndexPaths:.
So in your case, the row was deleted twice, and that caused the exception.
Note that you don't need beginUpdates/endUpdates here.
If you want to delete a row you need to delete managedObject only.
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.managedObjectContext deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
// handle error
}
}
Deleting the managed object triggers the NSFetchResultController delegate methods, and they will update the tableView.
Edit
You should implement NSFetchResultController delegate method
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath{
switch(type) {
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
////
default:
break;
}
Because when you work with data source like NSFetchedResultsController, all changes must come from there and your table only reflects them.

Error message deleting the last row of a section using NSFetchedResultsController

I've got an UITableView that uses an NSFetchedResultsController as it's data source. I use one of the fields for my sections. I enabled row deletion (using the swipe gesture). Works just fine.
The problem comes up when I delete the last row of a section. It does not crash on me but the console shows the following message:
CoreData: error: Serious application error. An exception was caught
from the delegate of NSFetchedResultsController during a call to
-controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section
after the update (1) must be equal to the number of rows contained in
that section before the update (1), plus or minus the number of rows
inserted or deleted from that section (0 inserted, 1 deleted) and plus
or minus the number of rows moved into or out of that section (0 moved
in, 0 moved out). with userInfo (null)
This is all pretty basic stuff:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[[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 NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[self configureCell:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
…
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[[self tableView] endUpdates];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
FHClass *foo = [[self fetchedResultsController] objectAtIndexPath:indexPath];;
[[self managedObjectContext] deleteObject:meal];
[self saveContext];
}
}
If I check -numberOfRowsInSection: I see that the number is steadily decreasing when deleting rows. Even to the point where it says 0.
What did I miss? Any hints? Stupid mistake on my part? ;-)
You should implement controller:didChangeSection:atIndex:forChangeType: to deal with section changes and then request the table view to deleteSections:.

Adding a row in TableView iOS

I´m quite new to iOS development and I´m having a terrible time by trying something that should be easy; to add an extra row in a TableView everytime the user clicks on one of the existing rows. There is no real purpose on that action, I´m just wanting to understand the behaviour of TableView.
So I did the following:
I used a Split View-based template and changed the number of rows to 30 in the RootViewController.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 30;
}
The method tableView:didSelectRowAtIndexPath looks in the following manner:
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
/*
When a row is selected, set the detail view controller's detail item to the item associated with the selected row.
*/
NSMutableArray* paths = [[NSMutableArray alloc] init];
NSIndexPath *indice = [NSIndexPath indexPathForRow:30 inSection:0];
[paths addObject:indice];
detailViewController.detailItem = [NSString stringWithFormat:#"Second Story Element %d with all its information and bla bla bla", indexPath.row];
[[self tableView] beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
}
When I execute the program and click on one of the elements, I receive the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (30) must be equal to the number of rows contained in that section before the update (30), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted).'
I did not change any other part of the code that the template provides.
I read quite extensively the documentation from Apple and the responses to the following questions:
Add a row dynamically in TableView of iphone
and
how to properly use insertRowsAtIndexPaths?
The second question seems to address the same problem, but I´m not capable to understand what is happening. What do they mean with dataSource? The response that I understand better says the following:
It's a two step process:
First update your data source so numberOfRowsInSection and cellForRowAtIndexPath will return the correct values for your post-insert data. You must do this before you insert or delete rows or you will see the "invalid number of rows" error that you're getting.
What does this update of the data source implies?
Sample code would be HIGHLY appreciated, because I´m totally frustrated.
By the way, all that I´m trying has nothing to do with entering the editing mode, has it?
You need to keep the count returned by tableView:numberOfRowsInSection: in sync!
So when you have 30 rows and then tell the tableview to insert a new row you need to make sure tableView:numberOfRowsInSection: will now return 31.
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section
{
return self.rowCount;
}
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.rowCount++;
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
}
In practice you would probably use an array to track your rows return [self.rows count]; etc
The answer is quite simple. When you want to modify a table view you need to perform two simple steps:
Deal with the model
Deal with table animation
You already perform the second step. But you have missed the first one. Usually when you deal with a table you pass it a data source. In other words some data to display within it.
A simple example is using a NSMutableArray (it's dynamic as the name suggests) that contains dummy data.
For example, create a property like the following in .h
#property (nonatomic, strong) NSMutableArray* myDataSource;
and in .m synthesize it as:
#synthesize myDataSource;
Now, you can alloc-init that array and populate it as the following (for example in viewDidLoad method of your controller).
self.myDataSource = [[NSMutableArray alloc] init];
[self.myDataSource addObject:#"First"];
[self.myDataSource addObject:#"Second"];
Then, instead of hardcoding the number of rows you will display (30 in your case), you can do the following:
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {
return [self.myDataSource count];
}
Now, in you didSelectRowAtIndexPath delegate you can add a third element.
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.myDataSource addObject:#"Third"];
[[self tableView] beginUpdates];
[self.tableView insertRowsAtIndexPaths:(NSArray *) paths withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
}
It looks like one big problem is with tableView:numberOfRowsInSection:. You need to return the correct number of rows in that method.
To do that, it's usually best to maintain an NSArray or NSMutableArray of items for the table view so in that function, you can say: return [arrayOfValues count];. Keep the array as a property of your view controller class so that it's readily accessible in all methods.
The array can also be used in cellForRowAtIndexPath:. If you have an array of NSString, you can say cell.text = [arrayOfValues objectAtRow:indexPath.row];.
Then, when you want to add an item to the table view, you can just add it to the array and reload the table, e.g. [tableView reloadData];.
Try implementing this concept and let me know how it goes.
You can Also do that for dayanamic table cell
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayStationStore count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIndentyfire;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIndentyfire];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIndentyfire];
}
cell.textLabel.text = [arrayStationStore objectAtIndex:indexPath.row];
return cell;
}
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Check if current row is selected
BOOL isSelected = NO;
if([tblStationName cellForRowAtIndexPath:indexPath].accessoryType == UITableViewCellAccessoryCheckmark)
{
isSelected = YES;
}
if(isSelected)
{
[tblStationName cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
[arrayReplace removeObject:indexPath];
NSLog(#"array replace remove is %# ",arrayReplace);
}
else
{
[tblStationName cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
[arrayReplace addObject:indexPath];
NSLog(#"array replace add is %# ",arrayReplace);
}
return indexPath;
}

Change a UITableView row's section at runtime

I have a UITableView that is controlled by a NSFetchedResultsController.
Rows are organised into sections from a property of the row. But when I modify that property the application terminated with:
Invalid update: invalid number of sections.
Each row represents a shot in a film, wide shot, close up etc..
The row also contains which scene it belongs to and the sections are calculated from that.
Shot.m (CoreData entity)
#pragma mark Transient properties
- (NSString *)sectionIdentifier
{
return [self sceneNumber];
}
ShotsViewController.m (The UITableView)
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
id <NSFetchedResultsSectionInfo> theSection = [[self.fetchedResultsController sections] objectAtIndex:section];
return [theSection name];
}
I don't know what i'm supposed to do here. I've played around with the UITableView insertSections and deleteSections but I always get same invalid number of sections error.
Edit
it moves automatically to other existing sections:
- (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;
case NSFetchedResultsChangeUpdate:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
It worked after I added the
case NSFetchedResultsChangeUpdate:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
But now, say i've got two cells.
Cell1.sceneNumber = 1
Cell2.sceneNumber = 2
If I set Cell1.sceneNumber = 3 I get the error:
Invalid update: invalid number of sections. The number of sections contained in the table view after the update (3) must be equal to the number of sections contained in the table view before the update (2)
We'd need to see a lot more code to know what the problem is. However, one thing that might be useful is checking out the "Mastering Table Views" video from last year's WWDC. They go over the specifics of how to add/remove sections/rows and the order which you should make the change in your model/table (with clear diagrams).
Go to https://developer.apple.com/videos/wwdc/2010/ and after logging in, go to Application Frameworks > Session 128 - Mastering Table Views.

Resources