I need your help :(
I'm working on a iOS application in which i had to remove some rows and sections in a UItableView.
I'm on Xcode 4.
I associate my tableView with NSarray and dictionary, like this :
NSMutableArray *arrTemp1 = [[NSMutableArray alloc]
initWithObjects:#"Chris",nil];
NSMutableArray *arrTemp2 = [[NSMutableArray alloc]
initWithObjects:#"Bianca",nil];
NSMutableArray *arrTemp3 = [[NSMutableArray alloc]
initWithObjects:#"Candice",#"Clint",#"Chris",nil];
NSMutableArray *arrTemp4 = [[NSMutableArray alloc]
initWithObjects:#"Candice",#"Clint",#"Chris",nil];
NSMutableArray *arrTemp5 = [[NSMutableArray alloc]
initWithObjects:#"Candice",#"Clint",#"Chris",nil];
NSMutableArray *arrTemp6 = [[NSMutableArray alloc]
initWithObjects:#"Candice",#"Clint",#"Chris",nil];
NSMutableDictionary *temp = [[NSMutableDictionary alloc]
initWithObjectsAndKeys:arrTemp1,#"A",arrTemp2,
#"B",arrTemp3,#"C",arrTemp4, #"D", arrTemp5, #"E", arrTemp6, #"J",nil];
self.tableContents = temp;
self.sortedKeys =[[self.tableContents allKeys]
sortedArrayUsingSelector:#selector(compare:)];
When i need to delete a rows or a sectionm i'm using this code ->
(void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
NSMutableArray *listData = [self.tableContents objectForKey:
[self.sortedKeys objectAtIndex:[indexPath section]]];
NSUInteger row = [indexPath row];
[listData removeObjectAtIndex:row];
[tableView beginUpdates];
if ([listData count] > 0)
{
// Section is not yet empty, so delete only the current row.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
else
{
// Section is now completely empty, so delete the entire section.
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
}
[tableView endUpdates];
}
}
When i'm deleting rows, it works well..
When i'm deleting sections, i'm getting the following error :
" * Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit/UIKit-1912.3/UITableView.m:1030
2012-01-13 16:42:45.261 * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (6) must be equal to the number of sections contained in the table view before the update (6), plus or minus the number of sections inserted or deleted (0 inserted, 1 deleted).'"
(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [tableContents count]; // tableContent is a NSMUtableDictionnary*
}
- (NSInteger)tableView:(UITableView *)table
numberOfRowsInSection:(NSInteger)section
{
NSArray *listData =[self.tableContents objectForKey:
[self.sortedKeys objectAtIndex:section]];
return [listData count];
}
I'm getting crazy, and coming on stackOverflow to ask for help...
(Sorry for my poor english :( )
Thanks to everyone reading and answering !!
It looks like you're not actually deleting the dictionary key for the empty array when it contains no objects, thus your call to
// Section is now completely empty, so delete the entire section.
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationFade];
is causing the problem as you still have the same number of sections (dictionary keys) as before...
Try swapping these 2 lines around:
[listData removeObjectAtIndex:row];
[tableView beginUpdates];
So it should be:
[tableView beginUpdates];
[listData removeObjectAtIndex:row];
Related
I'm trying to delete section from UITableView.
Here is my code for adding object to array :
NSMutableArray *temp = [NSMutableArray new];
ClassName *section = [[ULPDPSection alloc] initWithTitle:#"Section-Title" rows:#[]];
[temp addObject:section];
self.sectionArray = [NSArray arrayWithArray:temp];
[self.tableView reloadData];
This is my numberOfRowsInSection code:
#property(nonatomic, strong) NSArray *sectionArray;
#property(nonatomic, strong) NSArray *rowsArray;//This is in ClassName
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
ClassName *section = [self.sectionArray objectAtIndex:section];
return section.rowsArray.count;
}
}
This is my code to delete section:
if(image.count < 5)
{
NSUInteger index = [self indexForSection:#"Section-Title"];
if(self.tableView.numberOfSections > index)
{
[self.tableView beginUpdates];
NSMutableArray *temp = [NSMutableArray arrayWithArray:self.sectionArray];
[temp removeObjectAtIndex:index];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex: index] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
}
}
I'm getting crash message when trying to delete section:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (5) must be equal to the number of sections contained in the table view before the update (5), plus or minus the number of sections inserted or deleted (0 inserted, 1 deleted).'
I am working on a project using Microsoft Azure services. In that while deleting a 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 (2) 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, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
Code for table load and delete row is as :
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//medicine list
if (section == 0) {
NSArray *sectionInfo = [self.fetchedResultsController1 fetchedObjects];
return [sectionInfo count];
}
//allergy list
else if (section == 1) {
NSArray *sectionInfo = [self.fetchedResultsController2 fetchedObjects];
return [sectionInfo count];
}
//notes list
else if (section == 2){
NSArray *sectionInfo = [self.fetchedResultsController3 fetchedObjects];
return [sectionInfo count];
}
else
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"mcell";
MedicationTableViewCell *cell = (MedicationTableViewCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Add utility buttons
NSMutableArray *rightUtilityButtons = [NSMutableArray new];
[rightUtilityButtons sw_addUtilityButtonWithColor: [UIColor colorWithRed:1.0f green:0.231f blue:0.188 alpha:1.0f] title:#"Delete"];
switch (indexPath.section) {
case 0: {
NSManagedObject *item = [self.fetchedResultsController1 objectAtIndexPath:indexPath];
cell.textLabel.text = [item valueForKey:#"name"];
break;
}
case 1: {
NSManagedObject *item = [self.fetchedResultsController2 objectAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0]];
cell.textLabel.text = [item valueForKey:#"name"];
break;
}
case 2: {
NSManagedObject *item = [self.fetchedResultsController3 objectAtIndexPath: [NSIndexPath indexPathForRow:indexPath.row inSection:0]];
cell.textLabel.text = [item valueForKey:#"title"];
break;
}
default:
break;
}
cell.rightUtilityButtons = rightUtilityButtons;
cell.delegate = self;
return cell;
}
- (void)swipeableTableViewCell:(SWTableViewCell *)cell didTriggerRightUtilityButtonWithIndex:(NSInteger)index {
// Delete button is pressed
NSIndexPath *cellIndexPath = [self.medicationsTableView indexPathForCell:cell];
//fetchingg data from local store first
NSManagedObject *item = [self.fetchedResultsController1 objectAtIndexPath:[NSIndexPath indexPathForRow:cellIndexPath.row inSection:0]];
NSMutableArray *keys = [[NSMutableArray alloc] initWithObjects:#"id", #"name", #"userId", nil];
NSMutableArray *values = [[NSMutableArray alloc] initWithObjects: [item valueForKey:#"id"], [item valueForKey:#"name"], [item valueForKey:#"userId"], nil];
//creating dictionary of data
NSDictionary *dict = [[NSDictionary alloc] initWithObjects:values forKeys:keys];
//calling delete fucntion
[self.authService deleteItem:self.syncTable withDict:dict completion:^{
//removing row from table view
[self.medicationsTableView reloadData];
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
}];
break;
}
Please tell where I am going wrong. Thanks in advcance!!!!
In - (void)swipeableTableViewCell: didTriggerRightUtilityButtonWithIndex:
You need to remove either
[self.medicationsTableView reloadData];
or
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
Because on first line when reloadData get called it's reload the tableview with new datasource and again calling deleteRowsAtIndexPaths tried to delete a rows which already removed by calling reloadData earlier.
The important thing is that you need to use either
[self.medicationsTableView reloadData];
or
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
The reason being, when the tableview's reload data is called the row which is removed from data source is deleted and after that when the delete row api is called for the same index path(where the row is already deleted causes the issue)
Also just before deleting the row from the table delete the object from the data source (self.fetchedResultsController1) and call
[self.medicationsTableView deleteRowsAtIndexPaths:#[cellIndexPath] withRowAnimation:UITableViewRowAnimationRight];
and after the above row's deletion animation is completed than you can call(but it is not required)
[self.medicationsTableView reloadData];
I have a problem with my UITableView where deleting the last row in the section terminates the app with an NSInternalInconsistencyException:
*** 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).'
My UITableView is populated with MPMediaItems from an MPMediaItemCollection (self.detailCollection). When the last one gets deleted I want to show a "No results found" label in a blank cell.
Here is my cellForRowAtIndexPath:
- (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...
if ([[self.detailCollection items] count] == 0) {
[tableView numberOfRowsInSection:1];
cell.textLabel.text = #"No results found";
//return cell;
} else {
MPMediaItem *song = (MPMediaItem *)[[self.detailCollection items] objectAtIndex:[indexPath row]];
if (song) {
cell.textLabel.text = [song valueForProperty:MPMediaItemPropertyTitle];
MPMediaItemArtwork *art = [song valueForProperty:MPMediaItemPropertyArtwork];
cell.imageView.image = [art imageWithSize:CGSizeMake(64, 64)];
}
}
return cell;
}
Here is my code for deleting the rows:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
PlaylistData *pData = [PlaylistData getInstance];
NSMutableArray *tempArray = [[self.eventDictionary valueForKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]] mutableCopy];
NSMutableArray *newArray = [[NSMutableArray alloc] init];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
// Delete the row from the data source
[tempArray removeObjectAtIndex:indexPath.row];
[self.eventDictionary setValue:tempArray forKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]];
[[NSUserDefaults standardUserDefaults] setValue:self.eventDictionary forKey:#"Playlist Items"];
[[NSUserDefaults standardUserDefaults] synchronize];
if ([tempArray count] == 0) {
[tableView numberOfRowsInSection:1];
}
for (int i=0; i<[tempArray count]; i++) {
NSString *pID = [NSString stringWithFormat:#"%#", [tempArray objectAtIndex:i]];
unsigned long long ullvalue = strtoull([pID UTF8String], NULL, 0);
NSNumber *UniqueID = [NSNumber numberWithUnsignedLongLong:ullvalue];
MPMediaQuery *cellQuery = [[MPMediaQuery alloc] init];
[cellQuery addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:UniqueID forProperty:MPMediaItemPropertyPersistentID]];
for (MPMediaItem *item in [cellQuery items]) {
[newArray addObject:item];
}
[cellQuery release];
}
if (![newArray count] == 0) {
self.detailCollection = [[MPMediaItemCollection alloc] initWithItems:newArray];
[tableView numberOfRowsInSection:[self.detailCollection count]];
} else {
[tableView numberOfRowsInSection:1];
[tableView reloadData];
}
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
And here is my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if ([[self.detailCollection items] count] == 0 || [self.detailCollection items] == nil || [self.detailCollection items] == NULL) {
return 1;
}
return [[self.detailCollection items] count];
}
My question is: Why isn't it creating the "No results found" cell when self.detailCollection is == 0?
I think you want something to the effect of:
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if ([newArray count] == 0) {
[tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
However, a simpler solution would be to just add a label to your table view. Unless there is some specific reason that you need an actual UITableViewCell to display "No results found".
UILabel *label = [[UILabel alloc] init];
CGRect frame = CGRectMake(0.0, 0.0, 320.0, 44.0);
label.frame = frame;
label.text = #"No results found";
[self.tableView addSubview:label];
One solution that I would recommend is to use a table footer view rather than a new cell. Basically, add a footer to your table that is only visible when the cell count is 0.
You can override the method
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
to get a footer.
When deleting and adding objects, check the new count and then adjust the visibility of the footer from there.
You are calling numberOfRowsInSection in a couple of places. You should never be calling it, it's a call back hook that you implement and the system calls.
The cleanest solution to do this would be to set self.tableView.tableFooterView when rows = 0
#interface UITableView : UIScrollView <NSCoding> {
...
#property(nonatomic,retain) UIView *tableFooterView;
// accessory view below content. default is nil. not to be confused with section footer
- (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if(editingStyle == UITableViewCellEditingStyleDelete) {
//Get the object to delete from the array.
NSLog(#"%#",[[_arrayForTable valueForKey:#"IDS"] objectAtIndex:[indexPath row]] );
NSInteger myInteger = [[[_arrayForTable valueForKey:#"IDS"] objectAtIndex:[indexPath row]] integerValue];
NSNumber *selRow = [NSNumber numberWithInt:myInteger];
NSLog(#"%#",selRow);
[self removeCell:selRow]; // this is working perfectly it gets remove from database
//Delete the object from the table.
// app get crash here crash report is pested below
[self.table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (void) removeCell:(NSNumber *)selRow{
NSLog(#"_arrayForTable%#",_arrayForTable);
NSInteger myInteger = [selRow integerValue];
[_arrayForTable removeObjectAtIndex:myInteger];
NSLog(#"_arrayForTable%#",_arrayForTable);
if(deleteStmt == nil) {
const char *sql = "delete from PersonNamesAndBirthDates where ID = ?";
NSLog(#"%s",sql);
if(sqlite3_prepare_v2(database1, sql, -1, &deleteStmt, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating delete statement. '%s'", sqlite3_errmsg(database1));
}
NSLog(#"%d",myInteger);
//When binding parameters, index starts from 1 and not zero.
sqlite3_bind_int(deleteStmt, 1, myInteger );
if (SQLITE_DONE != sqlite3_step(deleteStmt))
NSAssert1(0, #"Error while deleting. '%s'", sqlite3_errmsg(database1));
sqlite3_reset(deleteStmt);
}
* Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1046 2013-04-08
17:01:13.069 birthdate reminder 2[583:15803] * 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 (12) must be
equal to the number of rows contained in that section before the
update (12), 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).'
please suggest something
my numberofrow method is below
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return _arrayForTable.count;
}
app is not crashing everytime sometime works sometime crash
Ok, so as I see it, you are getting the ID of a specific row with:
NSInteger myInteger =
[[[_arrayForTable valueForKey:#"IDS"] objectAtIndex:[indexPath row]] integerValue];
NSNumber *selRow = [NSNumber numberWithInt:myInteger];
And then, you're using the ID of that specific row, as the index to remove it from the array.
NSInteger myInteger = [selRow integerValue];
[_arrayForTable removeObjectAtIndex:myInteger];
That's where things go wrong. If the array has the same order as your TableView, you need to remove indexPath.row from it. The ID is necessary for your database.
Try this:
- (void)tableView:(UITableView *)tv commitEditingStyle :(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if(editingStyle == UITableViewCellEditingStyleDelete) {
//Get the object to delete from the array.
NSLog(#"%#",[[_arrayForTable valueForKey:#"IDS"] objectAtIndex:[indexPath row]] );
NSInteger myInteger = [[[_arrayForTable valueForKey:#"IDS"] objectAtIndex:[indexPath row]] integerValue];
NSNumber *selRow = [NSNumber numberWithInt:myInteger];
NSLog(#"%#",selRow);
[self.table beginUpdates];
[self removeCell:selRow]; // this is working perfectly it gets remove from database
//Delete the object from the table.
// app get crash here crash report is pested below
[self.table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.table endUpdates];
}
}
Notice the [self.table beginUpdates]; and [self.table endUpdates]; around the lines of code which alter the tableview.
IT"S WORKING
at and of your commitEditingStyle method add this line
[TableViewForFirstView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
I have a tableview with several sections. I would like to be able to move rows from one section into another and to delete a section once it has no rows. I am trying to do this through moveRowAtIndexPath but the code I have doesn't work and throws an NSRangeException exception.
Here is a code sample:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
NSUInteger fromSection = [fromIndexPath section];
NSUInteger fromRow = [fromIndexPath row];
NSString *fromKey = [self.keys objectAtIndex:fromSection];
NSMutableArray *fromEventSection = [self.eventsDict objectForKey:fromKey];
NSUInteger toSection = [toIndexPath section];
NSUInteger toRow = [toIndexPath row];
NSString *toKey = [self.keys objectAtIndex:toSection];
NSMutableArray *toEventSection = [self.eventsDict objectForKey:toKey];
id object = [[fromEventSection objectAtIndex:fromRow] retain];
[fromEventSection removeObjectAtIndex:fromRow];
[toEventSection insertObject:object atIndex:toRow];
[object release];
// The above code works just fine!
// Try to delete an empty section. Here is where trouble begins:
if ((fromSection != toSection) && [fromEventSection count] == 0) {
[self.keys removeObjectAtIndex:fromSection];
[self.eventsDict removeObjectForKey:fromKey];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:fromSection] withRowAnimation:UITableViewRowAnimationFade];
}
I have had luck performing the deleteSections method in a block subsequent to the end of the moveRowAtIndexPath method by wrapping the removal in a dispatch_async.
dispatch_async(dispatch_get_main_queue(), ^{
if ((fromSection != toSection) && [fromEventSection count] == 0) {
[self.keys removeObjectAtIndex:fromSection];
[self.eventsDict removeObjectForKey:fromKey];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:fromSection] withRowAnimation:UITableViewRowAnimationFade];
}
});
This also gave me some grief. I've had success with performing the section deletion using a delay.
Here's how I got it to work - assuming you're using a store to contain all the objects, and the store has methods to move the items:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
NSInteger beforeSectionCount = [store sectionCount];
[store moveObject:fromIndexPath toIndexPath:toIndexPath];
if (beforeSectionCount > [store sectionCount]
[self performSelector:#selector(deleteSection:) withObject:fromIndexPath: afterDelay:0.2]
}
- (void)deleteSection:(NSIndexPath *)indexPath {
[[self tableView] beginUpdates];
[[self tableView] deleteSections:[NSIndexSet indexSetWithIndex:[indexPath section]]
withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] endUpdates];
}