Deleting Rows from a UITableView That has Multiple Sections - iphone

I'm developing an iPad Application, in which one of the screens has an embedded tableview with multiple sections. Each section is populated by its own array (array1, and array2).
I've created a button that puts this table into editting mode. However, I need to change my
$tableView:commitEditingStyle:forRowAtIndexPath
somehow to determine what section the selected row is in, and delete the entry from the associated array as well. Does anyone have any ideas?
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
if(editingStyle == UITableViewCellEditingStyleDelete){
//This is the line i need to change...
[array1 removeObjectAtIndex:indexPath.row];
[myTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}

Looks like you need to remove the row from the correct array... using a switch statement on the sections might work (but only if there's a set number of sections, which you seem to indicate there is)
So where you're removing the object from the array, make sure you're removing it from the correct array based on the section.
switch (indexPath.section) {
case 0
[array0 removeObjectAtIndex:indexPath.row];
break;
case 1
[array1 removeObjectAtIndex:indexPath.row];
break;
}
etc.

Do like this... Get the required section using
NSInteger section = [indexPath section];
if (section == 0)
{
// write your code for deleting rows in your 1st section
}
if (section == 1)
{
// write your code for deleting rows in your 2nd section
}
// This is the idea...

The indexPath variable has the properties row and section. So, you can do:
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{ if(editingStyle == UITableViewCellEditingStyleDelete){
if (indexPath.section == 1) {
// do something
}
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
is the method you need to determine which cell you have been selected.
If you just want to determine the actual section which is shown you can do that with indexPath.section
NSInteger section = [indexPath section];

Try this:
[[self.mainArrayList objectAtIndex:indexPath.section] removeObjectAtIndex:indexPath.row];

The indexPath parameter of the commitEditingStyle:... method has the section and row of the cell. Perhaps I'm misunderstanding the question, can you clarify what you mean by 'delete it from the associated array' - do you have one array for each section in your table?

Related

At canEditRowAtIndexPath Method, reloadData of UITableView not work properly?

In my application, I reload my TableView ([tablView reloadData];) after delete row from TableView then canEditRowAtIndexPath Method alway call for (pervious) total number Of Rows.
For Example:
If i have 5 Rows on my TableView, then i delete 1 row from tableView. After deleting, I reload my TableView ([tablView reloadData]) but canEditRowAtIndexPath Method calls 5 time instead of 4 times ??
So i always got Following Error:
Terminating app due to uncaught exception 'NSRangeException', reason: '* **-[__NSArrayM objectAtIndex:]: index 5 beyond bounds [0 .. 4]'
I also tried to reload table after some delay (using NSTimer) but it also not worked for me.
I put some code here:
I apply canEditRowAtIndexPath on specific row which #"status" isEqualToString:#"directory" such like,
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%d", self.listOfSounds.count);
// Return NO if you do not want the specified item to be editable.
if([[[self.listOfSounds objectAtIndex:indexPath.row] objectForKey:#"status"] isEqualToString:#"directory"])
return YES;
else
return NO;
}
Code of delete row:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.sql_ deleteSoundFileAudioTableWhereMainID:[[self.listOfSounds objectAtIndex:indexPath.row] objectForKey:#"main_id"]]; /// delete record from DB
[self.listOfSounds removeObjectAtIndex:indexPath.row]; /// delete record from Array
[self updateListofSoundsFile]; /// Custom method
}
}
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO; // i also tried to return YES;
}
Here updateListofSoundsFile is my custom method code is :
-(void)updateListofSoundsFile
{
if(self.listOfSounds.count > 0)
[self.listOfSounds removeAllObjects];
self.listOfSounds = [self.sql_ getAllDataFromAudioTable]; // get all record from DB
NSLog(#"%d",self.listOfSounds.count);
[self.tblView reloadData];
}
Please Give any suggestion, How can i solve this issue ?
Thanks :)
you need to remove raw from tableview also befor remove item from array and reload data using this line becouse remove item from array but not tableview.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.sql_ deleteSoundFileAudioTableWhereMainID:[[self.listOfSounds objectAtIndex:indexPath.row] objectForKey:#"main_id"]]; /// delete record from DB
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.listOfSounds removeObjectAtIndex:indexPath.row]; /// delete record from Array
[self updateListofSoundsFile]; /// Custom method
}
}
I ran into this same problem. The issue was that I deleted the object of the cell but when I used the reloadData method of the tableView, my canEditRowAtIndexPath method was not being called resulting in being able to edit cells that I do not want edited. The true fix was not calling the deleteRowsAtIndexPaths method, but the [tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; method.
Basically here is what was happening:
When I called reloadData:
The raw data was not being removed from the tableView as Nitin said. Therefore this is not the solution.
When I called deleteRowsAtIndexPaths:
iOS detected a discrepancy between the underlying data and the number of cells (because I had already removed the underlying object). The result was a crash which is also not the solution (obviously).
Now for The Fix!
When I called reloadRowsAtIndexPaths:
This caused the tableView to simply reload that single cell AND it got rid of the raw data. This is the solution.
Rather than removing the dataSource object, then trying to remove the cell that is essentially backed by nothing at this point (which causes a crash), simply remove the dataSource object, then reload that indexPath of the tableView
Here is the general format of the method:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[myDataSourceArray removeObjectAtIndex:indexPath.row];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
This is the first time I ran into the issue with the residual raw data that is not removed by simply calling the reloadData method. This is certainly the most elegant solution that I have seen thus far.
Happy Coding! I hope this helps.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.sql_ deleteSoundFileAudioTableWhereMainID:[[self.listOfSounds objectAtIndex:indexPath.row] objectForKey:#"main_id"]]; /// delete record from DB
[self.listOfSounds removeObjectAtIndex:indexPath.row]; /// delete record from Array
[self updateListofSoundsFile]; /// Custom method
}
[self.tblView reloadData];
}
When it comes to me, I analysed as below:
As commented by rmaddy on Nitin Gohel's answer:
You should always remove the data from the data source before updating the table.
I feels this is the ideal way.
If you are going to call reloadData, there is no reason at all to first call deleteRowsAtIndexPath.
This is also looks correct.
I analysed ambiguity, if I write reloadData its crashishing but once I write deleteRowsAtIndexPath it worked well.
Hope someone will emphasise on this issue for its causes etc.
the 'removeObjectAtIndex:indexPath' takes some time and I suspect your [self.tblView reloadData] is being called early. I tried a sample code and found success with [UiTableView beginUpdates] and [UiTableView endUpdates] you may also avoid crash if you put a little delay before the reloading or deleting rows haven't tried it though
[tableTable beginUpdates];
[tableArray removeObjectAtIndex:indexPath.row];
[tableTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableTable endUpdates];

how to delete the cell in table view

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.

Adding dynamic sub-rows by selecting tableview row in tableview iPhone errors?

I want to add row dynamically. I have tableview list of building names. If some one choose building(didSelectRowAtIndexPath) then respective floors of building should get added dynamically as subrow. Its like maximizing and minimizing the subrow on respective building list selection. How do I do this. Thanks in advance...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// There is only one section.
if (tableView == indoortable || tableView == indoortable_iPad)
{
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of time zone names.
if (tableView == indoortable || tableView == indoortable_iPad)
{
return [indoorZones count];
}
}
cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == indoortable || tableView == indoortable_iPad)
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleGray; //cell bg
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Set up the cell.
//cell.textLabel.text = [copyListOfItems objectAtIndex:indexPath.row];
cell.textLabel.text =[indoorZones objectAtIndex: indexPath.row];
//[cell setIndentationLevel:[[self.indoorZones objectAtIndex:indexPath.row] intValue]];
return cell;
}
}
didSlectRowAtIndexPath method:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
zonesFloor = [[NSMutableArray alloc]init];
zonesFloorA = [[NSArray alloc] initWithObjects:#"Gr fl",#"1st fl",#"2nd fl",nil];
[zonesFloor addObject:zonesFloorA];
if (tableView == indoortable )
{
NSUInteger i=indexPath.row+1;
for (NSArray *count in self.indoorZones) //app is crashing here giving error.......Collection <__NSArrayM: 0x4b1d550> was mutated while being enumerated.
{
[zonesFloor addObject:[NSIndexPath indexPathForRow:i inSection:0]];
[self.indoorZones insertObject:zonesFloor atIndex:i++];
}
[[self indoortable] beginUpdates];
[[self indoortable] insertRowsAtIndexPaths:(NSArray *)zonesFloor withRowAnimation:UITableViewRowAnimationNone];
[[self indoortable] endUpdates];
}
if (tableView == indoortable_iPad )
{
//some logic
}
[tableView deselectRowAtIndexPath:selectedIndexPath animated:NO];
}
It Gives following error [__NSArrayI compare:]: Or [NSIndexPath _fastCStringContents:]: unrecognized selector sent to instance. I tried many ways but may be I am lacking somewhere. Please suggest. thanks in advance.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//NSIndexPath *selectedIndexPath = [self.indoortable indexPathForSelectedRow];
zonesFloorA = [[NSArray alloc] initWithObjects:#"Gr fl",#"1st fl",#"2nd fl",nil];
if (tableView == indoortable )
{
for (NSString *str in zonesFloorA) {
[indoorZones addObject:str];
}
//[[self indoortable] beginUpdates];
//[[self indoortable] insertRowsAtIndexPaths:(NSArray *)zonesFloor withRowAnimation:UITableViewRowAnimationNone];
//[[self indoortable] endUpdates];
}
if (tableView == indoortable_iPad )
{
//some logic
}
[tableView reloadData];
}
may this meet your requirement
Okay, so not to sound mean, but there are almost too many issues here to count.
Let's start with a basic explanation of how tableView's work so that you can start to fix this:
First, the tableView asks how many sections are in the table by calling:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
}
In your code, you simply tell it that it has one section. However, in your later code, when you try to add rows to your table, you tell it that you want to add your rows to a second section (with an index of 1). Therefore, you either need to add these rows to section 0 instead, or update the above method to tell it that, sometimes, there are two sections.
Second, the tableView asks how many rows are in each section of the table by calling:
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
}
In your code, you are simply returning the number of zones. However, like above, you need to include the rows that you have added to your table. If you are adding them to a different section, then you need to return different values, depending on how many rows are in the section with the index asked for in the section variable. If they are all in the same section, then you need to add them up and return the correct value.
Third, the tableView asks for an actual cell for the row by calling:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
}
In your code, you are only returning a cell which has data populated by the indoorZones array, but you also need to supply cells which are configured properly for the specific zone/floor. Again, you either need to determine this by section number or row number as appropriate.
Finally, when you click on a row, the tableview tells you by calling the following method:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
In this method, you need to update your data source that is used by the previous functions so that when they get called again, they will provide the correct data. Your data source must mirror the way that you want your table view to look. In your case, you have an array called indoorZones. This is good if you just want to display a list of zones, which is what you start off doing. However, when you want to add more rows, you need to add more rows to your data source first, so that when the tableView starts this process over, it is already there.
If you want everything to stay in one section, then I would come up with a data source that can include both types of rows, and be able to distinguish between them so that cellForRowAtIndexPath can create the proper type of cell and return it.
If you want two sections, then I would add a second array for the second section (since it is not the same type of data) and return the appropriate values in each of these methods, based on which array you need to use for that section.
I hope this helps!

Deleting a cell from UITableView - error

In my iPhone app, I have a UITableView with the following method
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [tableArrays count];
}
Note that tableArrays is a class variable (an NSMutableArray of NSMutableArrays - representing one NSMutableArray for each section in my tableView). Now, that UITableView supports editing, and I have the following method
- (void)tableView:(UITableView *)theTableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
...some code...
NSLog(#"tableArrays count is %i",[tableArrays count]);
[tableArrays removeObjectAtIndex:indexPath.section];
NSLog(#"tableArrays is now %#",tableArrays);
NSLog(#"tableArrays count is now %i",[tableArrays count]);
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
So, I run the app and I have two sections in my table view. Row deletion works fine except when I delete the last row in a section. When I delete the last row of section 1, I see the following output, as per the code above:
tableArrays count is 2
tableArrays is now (("Blah1","Blah2"))
tableArrays count is now 1
So clearly, my tableArrays count decreases by one, but I get the following error:
...The number of sections contained in the tableView after the update (1) must be qual to the number of sections contained in the table view before the update (2), plus or minus the number of sections inserted or dleted (0 inserted, 0 deleted).'
I guess in -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; you are returning the total count of objects and you want to display only one section.
Try returning one in this method.
You should also return [tableArrays count]in -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;.
It should work better like this. Also what is section in
[tableArrays removeObjectAtIndex:section]; ? Did you mean indexPath.section ?
Thank for the comment, so the method that is returning the number of section is correct, but you to do like that to delete a cell.
NSLog(#"tableArrays count is %i",[tableArrays count]);
NSMutableArray *sectionArray = [tableArrays objectAtIndex:indexPath.section];
[sectionArray removeObjectAtIndex:indexPath.row];
//[tableArrays removeObjectAtIndex:indexPath.section];
NSLog(#"tableArrays is now %#",tableArrays);
NSLog(#"tableArrays count is now %i",[tableArrays count]);
The key is to use
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
when you are trying to delete the last row in a section.

Delete UITableView row issue

I'm having a problem that I see a lot of people having... however none of the fixes are working for me. But I did notice something odd happening when I put NSLog commands in the code.
I have the standard delete method:
- (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle forRowAtIndexPath: (NSIndexPath *) indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.recipes removeObjectAtIndex: indexPath.row];
[tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject: indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
}
recipes is the array that holds the data source.
Theoretically this should work just fine, but I get the error:
invalid number of rows in section 1. 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 (10), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted).
But I know where this is falling apart, when I add an NSLog into the numberOfRowsInSection, I see that the method is being called twice from the method above.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSLog(#"THIS CALLED TWICE %i",[recipes count] );
return [recipes count];
}
Anyone know what else could cause the numberOfRowsInSection method to fire twice?
Thank you for your time.
After writing this, I saw that I had the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 2;
}
Apparently, numberOfRowsInSection is called for each section... changing the code to:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
Fixed the issue.