I've been working on creating a UITableView that has its content loaded from a NSUserDefaults object. Whenever I attempt to delete a cell/section, however, the program crashes and spits out the following:
*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1030
*** 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 (2) must be equal to the number of sections contained in the table view before the update (3), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).
No matter how much I read up on this error and try to fix it, I've been stuck here for the past few hours. Here is my method that is called when the user taps the "delete" button:
(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
//[[[NSUserDefaults standardUserDefaults] objectForKey:#"rawArray"] removeObjectAtIndex:indexPath.row];
//[[[NSUserDefaults standardUserDefaults] objectForKey:#"rawDates"] removeObjectAtIndex:indexPath.row];
NSMutableArray *rawArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"rawArray"];
NSMutableArray *rawDates = [[NSUserDefaults standardUserDefaults] objectForKey:#"rawDates"];
[rawArray removeObjectAtIndex:indexPath.row];
[rawDates removeObjectAtIndex:indexPath.row];
[[NSUserDefaults standardUserDefaults] setObject:rawArray forKey:#"rawArray"];
[[NSUserDefaults standardUserDefaults] setObject:rawDates forKey:#"rawDates"];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationRight];
}
}
and here are the methods used to determine number of rows and cells:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return [[[NSUserDefaults standardUserDefaults] arrayForKey:#"rawDates"] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
return [[[NSUserDefaults standardUserDefaults] arrayForKey:#"rawDates"] objectAtIndex:section];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 1;
}
The last method in there returns a one because, as of right now, I plan on only have one cell per each section. Thank you for taking the time to look over here, and I really hope you can help me out! :/
Check out the Batch Insertion, Deletion, and Reloading of Rows and Sections
Basically you need to put any calls of these method
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
between
- (void)beginUpdates;
- (void)endUpdates;
e.g.
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationRight];
[tableView endUpdates];
Your dataSource also needs to reflect the changes by the time endUpdates is called
Personally I like to make a category to give a block based wrapper around this to
stop me forgetting to call endUpdates
it allows indentation to look right / be automatic
Category on UITableView
#interface UITableView (PSAdditions)
- (void)ps_updates:(void (^)(void))updateBlock;
#end
#implementation UITableView (PSAdditions)
- (void)ps_updates:(void (^)(void))updateBlock;
{
[self beginUpdates];
if (updateBlock) { updateBlock(); }
[self endUpdates];
}
Which would look like this
[tableView ps_updates:^{
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationRight];
}];
Related
Can some one suggest what is the problem in my code.....
self.modleClass.foldersAray have one object and tableview have one section and one row
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.tableview setEditing:YES animated:YES];
[self.modleClass.foldersAray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YEs];
[tableView reloadData];
}
}
I am getting error as below.
*** 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).'
Do like this,
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.modleClass.foldersAray removeObjectAtIndex:indexPath.row];
[tableView reloadData];
}
}
it is more enough.........
If your tableView is based on the foldersAray, then removing an object from this array and reloading the tableview will result in the deletion of this cell.
You could remove the line:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YEs];
This error means, that you must update you datasource (array with data, dictionary or whatever you use)
For example:
You have 5 cells before delete and 5 object in dataArray. Before call to deleteRowsAtIndexPaths: we must remove object from dataArray, that associated with this cell and only then call deleteRowsAtIndexPaths:. Also instead use [tableView reloadData], use
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
P.S. I see, that you remove object from your datasource, so check it carefully, maybe you miss something
If you want animation try this, if you dont want animation use #Mountain Lion's answer.
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.modleClass.foldersAray removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]withRowAnimation:UITableViewRowAnimationFade];
}
}
And if you have edit button on navigation, you should call
[self.tableView setEditing:YES animated:YES];
inside of your method to start editting.
IF the memory it takes does not matter for you... then you can dramatically delete (hide) cell by setting height to 0.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row%2) {
return 0;
}
return 100;
}
P.S: above dummy code will dramatically delete alternate cells.
My table doesn't update after removing an item with Edit / Delete or the swipe-delete gesture. If I do Edit / Delete, the Delete button doesn't disappear. It stays there in a pressed/stuck state. When I then click Done, the Delete button goes away. A screenshot of this is attached at the bottom of this post.
My numberOfRowsInSection code is executed when the app is started, but not after the deletion of an item.
I'm using a UITableViewController. The definition in my .h file:
#interface BNVFavoritesTableViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>{
BNVAppDelegate *appDelegate;
IBOutlet UITableView *myTable;
}
#property (retain, nonatomic) IBOutlet UITableView *myTable;
#end
Here's the relevant code from my .m file:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"number of rows:\t%d", [appDelegate.favoritesArray count]);
return [appDelegate.favoritesArray count];
}
- (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];
}
//Get the object from the array.
BNVFavorite *favoriteObj = [appDelegate.favoritesArray objectAtIndex:indexPath.row];
//Set the name.
cell.textLabel.text = favoriteObj.name;
// Set up the cell
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// If row is deleted, remove it from the list.
if (editingStyle == UITableViewCellEditingStyleDelete){
NSLog(#"before delete\t%d", [appDelegate.favoritesArray count]);
BNVFavorite *selectedObject = [appDelegate.favoritesArray objectAtIndex:indexPath.row];
NSString *selectedName = selectedObject.name;
// delete from sqlite database
[appDelegate removeFavoriteFromDB:selectedName];
// delete from memory array
[appDelegate.favoritesArray removeObjectAtIndex:indexPath.row];
// delete in user interface
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
NSLog(#"after delete\t%d", [appDelegate.favoritesArray count]);
}
}
When deleting, I see 'before delete' and 'after delete' messages in my log (as expected), but no 'number of rows' messages.
My datasource and delegate outlets from the Table View are connected to the TableViewController class. I also tried doing this in the ViewDidLoad method, but that didn't make a diferent. Forcing a [myTable reloadData]; at the end of my commitEditingStyle is also not helping.
Finally, here's a screenshot of that 'stuck' delete button.
http://i.stack.imgur.com/ukUu0.png
Clicking it a few times causes an NSRangeException/out of bounds error, indicating that the code from commitEditingStyle is executed.
After hours of searching, I found the problem. Turns out the referencing outlet from the tableview in the storyboard to myTable in my controller class didn't exist anymore. I must have removed it accidentally. I found out about it when I noticed the code started working when I replaced myTable with self.tableView.
You need to add your delete animation between beginUpdates and endUpdates.
[myTable beginUpdates];
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[myTable endUpdates];
Just check your connection in connection inspector for your table.
or if it is properly connected then
Reload your table after deleting record
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// If row is deleted, remove it from the list.
if (editingStyle == UITableViewCellEditingStyleDelete){
NSLog(#"before delete\t%d", [appDelegate.favoritesArray count]);
BNVFavorite *selectedObject = [appDelegate.favoritesArray objectAtIndex:indexPath.row];
NSString *selectedName = selectedObject.name;
// delete from sqlite database
[appDelegate removeFavoriteFromDB:selectedName];
// delete from memory array
[appDelegate.favoritesArray removeObjectAtIndex:indexPath.row];
// delete in user interface
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
NSLog(#"after delete\t%d", [appDelegate.favoritesArray count]);
[myTable reloadData];
}
}
I have my search display view controller based off TableSearch.
Normally, following expression gives selected index row:
self.tableView.indexPathForSelectedRow.row
However while in search mode, selected row can be obtained using:
self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row
I use the above indices to remove respective rows (from my back end arrays) soon after some processing - at the end of didSelectRowAtIndexPath. The arrays I use are listContent and filteredListContent - as per the Apple example.
My issue is, while in search mode, I remove a row using:
[self.filteredListContent removeObjectAtIndex: self.searchDisplayController.searchResultsTableView.indexPathForSelectedRow.row]
However, at the same time, I also want to remove the same object from self.listContent because when I return to non-search mode, that row should not appear.
I saw that self.tableView.indexPathForSelectedRow.row does not update regularly while in search results view. Instead, it gives the last selected row before I entered the search results view.
Off course, I can put some content into my array objects so that both indexes can be cross-referenced. But is there any efficient solution other than that? I think table view should have this mechanism.
And yes, I already do following in my viewWillAppear:
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
This is working fine for me...
#pragma mark - delete row
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
if(tableView == self.searchDisplayController.searchResultsTableView)
// searching mode
{
// find row (index) from listContent
arrayObject = [filteredlistContent objectAtIndex:indexPath.row];
NSUInteger fooIndex = [listContent indexOfObject: arrayObject];
NSLog(#"index listContent %u", fooIndex);
//remove from listContent
[self.listContent removeObjectAtIndex:fooIndex];
[self.tableView reloadData];
//remove from filteredlistContent
[self.filteredlistContent removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else // tableView mode
{
[self.listContent removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
}
i have one section table which contain the section and i want to delete the perticular cell swap and delete this cell and remove that cell from table and also remove from the array.
and how to animate that when the delete the cell.
for that i code below but which is not working please help to do this.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.reports removeObjectAtIndex:indexPath.row];
[tbl reloadData];
[tbl deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
After much more R&D then i build up new code and which is successfully Run
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[[[self.reports objectAtIndex:indexPath.section] valueForKey:#"events"] removeObjectAtIndex:indexPath.row];
[tbl deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section]] withRowAnimation:UITableViewRowAnimationLeft];
[NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector: #selector(tableReload) userInfo: nil repeats: NO];
}
}
-(void)tableReload{
[tbl reloadData]; }
Firstly you should not Reload table before deleting row from table. Secondly, in your case you have multiple sections and multiple rows in that section, I guess. So, your
[tbl deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
won't work.
Deleting object from array will work. You don't have to delete row from your table. Just reload table :
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[self.reports removeObjectAtIndex:indexPath.row];
[tbl reloadData];
}
But do take care that you are putting the correct indexNumber.
You are not nil terminating your NSArray and that is why the row isn't being deleted from your Table View.
The following line:
[tbl deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
Should actually be:
[tbl deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
withRowAnimation:UITableViewRowAnimationFade];
Notice the nil after IndexPath. arrayWithObjects should be a nil terminated list of Objects
I am trying to reload a uitableview when it scrolls to the bottom of the screen. I delete the first few rows and add more rows to the bottom. Before the news rows are added to the uitableview's data source, i call the [tableview reloaddata] method. This is because i want the tableview to display the row which were previously visible on it.
It reloads the data correctly but there is a sudden jerk in the tableview. It flashes which doesn't give a nice user experience. So my question is
How to update the uitableview
when few rows from the top are
deleted without having the
jerking/flashing effect?
The current visible row should be retained in the refreshed view also.
Any ideas? There are no crashes as i update the data source correctly.
TIA,
Praveen S
delete the data from the data source in tableView delegate.
-(void) tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *key = [keys objectAtIndex:indexPath.section];
NSMutableArray *nameSection =[names objectForKey:key];
int itemID=item.ID;
if ([nameSection count]==1) {
[self.keys removeObjectsInArray:[NSArray arrayWithObject:key]];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
else {
[nameSection removeObjectAtIndex:indexPath.row];
[self.names setValue:nameSection forKey:key];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
check this too