NSInternalInconsistencyException when trying to dynamically modify number of rows in UITableView - objective-c

I am attempting to use insertRowsAtIndexPaths so I can insert a table row. I am getting this 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 (0) must be equal to the number of rows contained in that section before the update (0), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted).'
What I am trying to do is to dynamically modify the number of rows in section in my attempt at rectifying the problem:
NSArray *indexPathIndex = [NSArray arrayWithObjects:[NSIndexPath indexPathForRow:0 inSection:0], nil];
[myTableView beginUpdates];
//***** HERE I AM TRYING TO DYNAMICALLY INCREASE NUMBER OF ROWS
[myTableView numberOfRowsInSection:1];
////////////////////////////////////////////////////////////
[myTableView insertRowsAtIndexPaths:indexPathIndex withRowAnimation:UITableViewRowAnimationTop];
[myTableView endUpdates];
Initially, my number of rows in section is 0. I try to increase this number in my efforts to address the aforementioned error however the code [myTableView numberOfRowsInSection:1] seems to have no effect.
Could somebody please suggest how I can fix this problem?
Thank you,
Victor.

You should not be setting the number of rows yourself, the UITableView manages that on its own. Remove the line.
myTableView numberOfRowsInSection:1];

Take a look at the UITableViewDataSource protocol, particularly the tableView:numberOfRowsInSection method. This will allow you to dynamically set the number of rows in the section.

A tableView has a datasource that is generally an array, that you can use to fill the cells of the tableView.
- (UITableViewCell*)tableView cellForRowAtIndexPath..... {
//Dequeueing or alloc-init-ing a cell
SomeClass *someObject = [someArray objectAtIndex:indexPath.row]; // Something
// like this, where someArray becomes the source array.
...
}
Now in some other part of your code, if you want to dynamically change the contents of the tableView, change the someArray and then change the tableView.
[someArray insertObjectAtIndex:someIndex];
[myTableView insertRowsAtIndexPaths....]; //The indexPath is generally
// consistent with the someIndex.

Related

OSX NSTableView insertRowAtIndexes

i already checked Using NSTableView insertRowsAtIndexes solution but It does not solve my problem.
i want insert row in nstableview at particular index(add dynamically)
index Set is valid, still it causes Program crash
NSIndexSet *indexSet=[NSIndexSet indexSetWithIndex:i];
[myTableView insertRowsAtIndexes:indexSet withAnimation:NSTableViewAnimationEffectFade];
1)is any thing wrong in my code?
2)Is there any another way to add row at particular index(add dynamically)?
The code is correct but first you have to insert the model object in the data source array to keep model and view in sync.
I got My mistake..... problem In other Code so This code is fine
But I want to add some points About insertRowsAtIndexes: method
Hope it will helps to other people
1)Dont called reloadData() because you are adding particular number of rows so calling reloadData() will reload all data and it will causes crash
2) Calling this method multiple times within the same beginUpdates and endUpdates block is allowed, and changes are processed incrementally
3)Most Important thing is indexSet must be within range
if you are Enter valid indexSet then The numberOfRows in the table view is automatically increased by the count of indexes.
4)you can select animation according to your need
Sample Code :
[yourTableView beginUpdates];
NSIndexSet* theIndexSet = [NSIndexSet indexSetWithIndex:[self.yourTableContents count]-1];
[yourTableView insertRowsAtIndexes:theIndexSet withAnimation:NSTableViewAnimationEffectFade];
[yourTableView endUpdates];

Deleting rows manually from UITableView in Xcode 6

I have the following code written for my delete button to delete selected rows from the UITableView.
-(IBAction)deleteItems:(id)sender {
NSArray *selectedCells = [self.autoCompleteView indexPathsForSelectedRows];
NSMutableIndexSet *indicesToDelete = [[NSMutableIndexSet alloc] init];
for (NSIndexPath *indexPath in selectedCells) {
[indicesToDelete addIndex:indexPath.row];
}
//arrayFromPlist is NSMutableArray
[autoCompleteView beginUpdates];
[autoCompleteView deleteRowsAtIndexPaths:selectedCells withRowAnimation:UITableViewRowAnimationAutomatic];
[autoCompleteView endUpdates];
[selectedObjects removeObjectsAtIndexes:indicesToDelete];
[autoCompleteView reloadData];
[alertMsg deleteConfirmation:#"Do you want to delete these items?"];
}
Please check the image for my UITableView and the delete button. I have kept the Editing property of my UITableview to "Multiple Selection during Editing" in the storyboard.
I get the following error when i press the Delete button shown in the screen.
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 (5) must be equal to the number of rows contained in that section before the update (5), plus or minus the number of rows inserted or deleted from that section (0 inserted, 2 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Not sure what is my mistake in the delete button code.
One definite problem is that you are removing the cells before you have removed the corresponding data from the model. That's wrong. Always change the model first; then change the cells in such a way as to match the model.

Curious about NSButtonCell state

I have a NSTableView with an NSTableColumn with an NSButtonCell (a checkbox) inside it, which generates a new instance of NSButtonCell each time a row is added, which I configured in IB. However, I'm curious why in the following chunk of code the second NSLog returns 0.
NSLog(#"%ld", (long)[[self.tableView preparedCellAtColumn:0 row:0]state]);
[[self.tableView preparedCellAtColumn:0 row:0]setState:1];
NSLog(#"%ld", (long)[[self.tableView preparedCellAtColumn:0 row:0]state]);
The fact that it returns 0 means that I am sending a message to an instance of NSButtonCell, right? So why doesn't setState: change the return value of the second NSLog?
If it was receiving nil it would also print 0, I would suggest trying this
NSLog(#"cell:%#", [self.tableView preparedCellAtColumn:0 row:0])
to ensure you are actually getting a valid cell object from the table view.
Where are you calling that code from? After the table is already being displayed? During initialisation?
If the former then there should be a cell available, if the latter then it may not have been created or reallocated from the pool yet.
Try the NSLog command above to ensure you are actually getting a cell back and not nil from the table view.

Dynamically updating sections of UITableView without using reloadData

First off I sincerely apologize if this has been answered. I have been battling with this for too long. I would appreciate any kind of help.
I am developing an application that gets data asynchronously from different web sources and parses it into objects that are handled by my main Model. This model provides information for a UITableView. When data is received and parsed, i need to modify the number of sections and rows in the table view. I first receive the data that decides the number of sections, and then asynchronously data that modifies the number of rows in each section and their content. I have the table views data sources set up.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return [self.sijainti.pysakit count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
//if ([[[self.sijainti.pysakit objectAtIndex:section] ajatStr] count] == 0) return 1;
NSLog(#"ajatStr of section: %d count: %d",section, [[[self.sijainti.pysakit objectAtIndex:section] ajatStr] count]);
return [[[self.sijainti.pysakit objectAtIndex:section] ajatStr] count];
}
The object names are in Finnish (long story). The second line of numberOfRowsInSection was commented to ease debugging. Now, because the data is dynamic i need to update the interface when i have data ready for display, or the user wont know about it until they scroll away and back again. I have a method for this:
- (void)updateRowWithCell:(SCPysakki *)valmisPysakki {
if (!valmisPysakki.isInArray) return;
int section = [self.sijainti.pysakit indexOfObject:valmisPysakki];
[self.tableView beginUpdates];
NSIndexSet *sectionSet = [NSIndexSet indexSetWithIndex:section];
[self.tableView reloadSections:sectionSet withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
This is called only after the number of sections is known. I do NOT want to use reloadData due to efficiency, and animation. I used to have this set up so that i had only rows and was just drawing rows inside of the rows. But once the rows became taller than the iphone screen, it became very unclear, because you couldn't see the title. So i made them sections, and the rows drawn real rows. Then i was suddenly faced with a problem. reloadRowsAtIndexPaths: withRowAnimation: worked absolutely fine but using reloadSections: withRowAnimation: gave me this exception:
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:1037
2012-09-17 04:11:27.769 pysappi[2351:f803] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. 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 (0), 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 was able to eliminate this by adding the [self.tableView endUpdate] and [self.tableView endUpdate] to the update method. But oh no, that only works if I:
Use reloadData after receiving the data deciding the number of sections (which in turn shortens the tableview so that after the whole update the user at the top of the table after every single update. I would rather update the sections dynamically, without using reloadData, except when removing Sections, but if I try to do this i get the exception again.)
update the sections after receiving data for only one Row (so if I try to update all the rows in the section once I've received all their data, I get the exception again.)
Now this creates a situation where creating a good user interface becomes impossible. I'm convinced that the underlying problem is not solved with the start and end updating calls, but that just fixes it for some very very odd reason.
I also tried adding the rows manually with insertRowsAtIndexPaths: and updating them manually also, but this returns the exact same error. During a long debugging progress i have been able to determine that this exception rises after cellForRowAtIndexPath: gets called and it returns a cell for one of the new rows that was added during the updating, even though the numberOfRowsInSection should have updated the amount of rows. I also suppose that if those few circumstances apply this is why the start and end updating methods make the row updating work.
The only thing that has worked outside of what i explained above, is using only reloadData, but that is just practically impossible considering the amount of updating.
This became a lot longer than i thought, and its 4.30 AM. Not the first time I'm up this late thanks to this exception. If you need more information ask. Thank you in advance.
(Answered in the comments. Converted to a community wiki answer. See Question with no answers, but issue solved in the comments (or extended in chat) )
#deleted_user wrote:
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 (0), - you had a section with zero rows and did an update with a row - cocoa didnt see that as an insert and failed you

How to get number of rows in NSIndexPath section

I want to get the number of rows in an NSIndexPath section. For all my searching here & in Apple's docs I can't find how to get this value. Any clues on it?
My specific purpose is to use that to determine whether or not the selected row is the last row in the section so if anyone has a suggestion for determining that by another means that would be as good.
Everything is in docs
numberOfRowsInSection:
int rows = [table numberOfRowsInSection:indexPath.section];
The only way is to ask the data source of the table:
NSUInteger rowCount = [tableView.dataSource tableView:tableView numberOfRowsInSection:indexPath.section];
Here's how I do it in my case:
The table section holds a list of objects stored in NSArray, hence the amount of rows in section is the amount of objects in the array. In your case - if row is not the object from array - just do the check
if ([indexPath row] + 1 == [_objectsArray count]) {
...
But if your items in table are not held in a collection, you can easily retrieve number of rows in section just by calling appropriate method on tableView object in your didSelectRow.. delegate method:
NSInteger numberOfRows = [tableView numberOfRowsInSection:[indexPath section]];
You can call tableView:numberOfRowsInSection: method of the UITableViewDataSource, passing the section number. However, it is your own code that produces this number based on what's in the model, so it may make sense to look at the implementation of tableView:numberOfRowsInSection: in your data source to see if you could get the same answer through an alternative path.
If you are talking about on a UITableView, you can ask it's dataSource, which is somewhat likely to be yourself :-)
[self.tableView.dataSource tableView: self.tableView numberOfRowsInSection: indexPath.section];
// or, if you are the dataSource...
[self tableView: self.tableView numberOfRowsInSection: indexPath.section];