Filtering sectioned UITableView - iphone

My application have UI similar to Phone.app->Recents: sectioned UITableView and a UISegmentedControl in the navigation bar. What I want to do is display full set of data if first section is selected and display filtered set of data if second section is selected.
When user selects second item in UISegmentedControl I delete specific rows from the table view. Here is the code:
[tableView beginUpdates];
NSMutableArray *indexPaths = [NSMutableArray array];
/// ... fill up indexPaths with row indexes
[tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
The code above works fine except for one serious issue: performance. Deleting 1500 out of 2200 rows takes about 20 seconds. That is obviously unacceptable. What is the best approach to filtering table view rows with animation?

For large changes to your data source, it is recommended that you use
[tableView reloadData]
instead of
[tableView beginUpdates];
// changes here ....
[tableView endUpdates];
EDIT: I haven't tried this approach myself, but consider altering only those rows that are contained in the collection of visible cells, perhaps with a buffer above and below. You can get the indexPaths of the visible cells by calling
[tableView indexPathsForVisibleRows];

How about using a two arrays? One of them is the full data set and the other one is the filtered dataset.
This way you can have two different tableviews, and depending on what the selected segment is, you could do a fade animation between the two tableviews. For example, lets say you select a segment:
-(void)switchTableViews
{
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
[UIView setAnimationDuration:0.5];
[UIView setAnimationDidStopSelector:#selector(hideTableView)];
switch (segmentedControl.selectedSegmentIndex)
{
case 0:
{
tableView1.alpha = 1.0;
tableView2.alpha = 0.0;
}
break;
case 1:
{
tableView1.alpha = 0.0;
tableView2.alpha = 1.0;
}
break;
default:
break;
}
[UIView commitAnimations];
}
- (void)hideTableView
{
switch (segmentedControl.selectedSegmentIndex)
{
case 0:
{
tableView1.hidden = NO;
tableView2.hidden = YES;
}
break;
case 1:
{
tableView1.hidden = YES;
tableView2.hidden = NO;
}
break;
default:
break;
}
}
Of course, this would mean setting up different code sets for the datasource methods but it's not as difficult as you think. Just use a simple if-else to check for which tableview is being set.

I definitely agree with #nduplessis that you should reload the dataSource rather than manipulating the view. I proposed a solution to a similar question here that would indeed cause your rows to slide up.
The basic idea is to call reloadSections:withRowAnimation: and in your UITableViewDataSource methods switch on the segmented control's selectedSegmentIndex.
Assuming your data is flat (only one section) it would look something like this:
- (IBAction)segmentSwitch:(id)sender
{
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (self.segmentedControl.selectedSegmentIndex)
{
default:
case 0:
return [self.allRows count];
case 1:
return [self.onlySomeRows count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id data;
switch (self.segmentedControl.selectedSegmentIndex)
{
default:
case 0:
data = [self.allRows objectAtIndex:[indexPath row]];
break;
case 1:
data = [self.onlySomeRows objectAtIndex:[indexPath row]];
break;
}
//TODO: use data to populate and return a UITableViewCell...
}

Related

Edit animation UITableViewStyle like addressbook

I need some help with UITableView. I'm looking for the best solution for creating edit table functionality. I have UITableViewController with data and two modes:
Edit mode: All fields (like first name, last name, phone, web page etc...)
View mode: Show only filed rows.
The difficult thing is to animate the rows when a user clicks the edit button.
I want the same animation we have in the address book app on iPhone.
Something like this in simple.
//original position and maybe hidden
someView.frame = CGRect(0,0,0,0);
someView.alpha = 0.0f;
[someView setHidden:YES];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
//what you want it comes out finally, changed position and visible
[someView setHidden:NO];
someView.frame = CGRect(100.0f,100.0f,0,0);
someView.alpha = 1.0f;
[UIView commitAnimations];
Use
[tableView setEditing: YES animated: YES];
to make the UItableView in editing mode.Which give you show and hide the button when you swipe on the row.if your animation is something please let me know.
I have solution!
Help from this article
So you need add something like this (thanks Sandy)
- (void)setEditing:(BOOL)editing animated:(BOOL)animated{
[super setEditing:editing animated:animated];
NSArray *paths = [NSArray arrayWithObject:
[NSIndexPath indexPathForRow:1 inSection:0]];
if (editing) {
[[self tableView] insertRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
}else {
[[self tableView] deleteRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
}
}
After that
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
will be colled, and you can edit list of cells and their appearance.
in the case below, I have 2 sections, first section contains a series of settings, first row is a default setting, always there and can't be reordered, and second one contains just one row like 'Add...', editing is only for reordering and deleting so in edit mode I remove the first setting, and remove the 2nd section, and it animates smoothly if you club all your insert/delete within a beginUpdates/endUpdates on the tableview. So for you it would be just the opposite, adding more rows/sections when editing
In Normal mode I have :
Countries (<-- 1st section)
- World
- Argentina
- USA
(<-- 2nd section)
- Add Countries...
In Edit mode I have :
Countries (<-- 1st section)
- Argentina = (can be removed/reordered)
- USA = (can be removed/reordered)
Code looks like:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
NSArray *paths = [NSArray arrayWithObjects:
[NSIndexPath indexPathForRow:0 inSection:0],
nil];
[self.tableView beginUpdates]; // Club all updates together
if (editing)
{
if( [[CCPollSettings countryCodes] count] < 2) // No country setting
// Remove complete section
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
else // Remove first default row
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
// Remove 'Add...' Section
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
// .... Do more stuff after
} else {
if( [[CCPollSettings countryCodes] count] < 2) // No country setting yet
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
else // add back default row
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
// Add back 'Add...' Section
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
}
[self.tableView endUpdates]; // Club all updates together
}

selectRowAtIndexPath only works when allowsMultipleSelection is YES

I have a UITableViewController that is populated dynamically depending on a previous selection by the user. In some cases, the user should be able to select multiple rows in the table but in other cases he should not. I want to automatically select the first row in either case, but I have found that it only works when I have allowsMultipleSelection = YES.
The following code is called during -(void)viewDidLoad
switch(self.field)
{
case people:
self.options = (NSArray *)[data objectForKey:#"People"];
self.tableView.allowsMultipleSelection = YES;
enter code here break;
case place:
self.options = (NSArray *)[data objectForKey:#"Places"];
self.tableView.allowsMultipleSelection = NO;
break;
case reason:
self.options = (NSArray *)[data objectForKey:#"Reasons"];
self.tableView.allowsMultipleSelection = NO;
break;
}
[self.tableView reloadData];
NSIndexPath *zeroPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView selectRowAtIndexPath:zeroPath animated:NO scrollPosition:UITableViewScrollPositionNone];
This works correctly for the "People" case but not for the others. I have tried setting allowMultipleSelection to YES and that solves the problem of the selection, but it is not acceptable in terms of the application logic.
Am I missing something about how these functions work? How can I programmatically select a row in a single selection table?
Fixed with the following from the template:
// Uncomment the following line to preserve selection between presentations.
self.clearsSelectionOnViewWillAppear = NO;
Still not sure exactly why it didn't work before.
for (NSIndexPath *indexPath in [self.tableView indexPathsForSelectedRows]) {
[self.tableView deselectRowAtIndexPath:indexPath animated:NO];
}
[self.tableView selectRowAtIndexPath:zeroPath animated:NO scrollPosition:UITableViewScrollPositionNone];

Strange animation when moving last row out of section and deleting section

I have a multi-section tableview. In edit mode I allow rows to be moved from one section to another. Once the final row is removed from one section I delete that section. So I am using deleteSection inside moveRowAtIndexPath.
When the final item is moved from the section, the section header disappears as planned. But there is a very strange animation bug, where the moved row seems to 'merge' with the row it is dropped above, and an empty row is displayed at the bottom of the 'to' section (probably because the numberOfRows for that section is correct, but 2 rows are in the same position). Even stranger, when I click the reorder control for this row (not moving the item, simply touching and releasing), the two items 'unmerge'.
I have posted a video demonstrating this.
I have tried wrapping my data changes and view changes in begin/end updates, but to no avail.
I have uploaded a test project here, and I will also post the code below. A couple of points:
I have tried to replicate my data source's format in the demo project, in case this is where the problem originates. The key thing is that my source is a composite array of two other arrays (though I can't see why this would be an issue).
To see the behavior in question, move the two rows in the bottom section, up into the top section. Don't drop them in the last row on the top section though, since this seems to work ok.
Moving rows the other way, from the top section to the bottom section, is buggy in this demo project.
Code (all of this is in the demo project):
I set up my arrays in loadView:
- (void)loadView{
array1 = [NSMutableArray array];
[array1 addObject:#"test 0"];
[array1 addObject:#"test 1"];
[array1 addObject:#"test 2"];
array2 = [NSMutableArray array];
[array2 addObject:#"test a"];
[array2 addObject:#"test b"];
[super loadView];
}
I also have a method that returns a combination of these arrays - this is used as the data source:
- (NSMutableArray *)sourceArray{
NSMutableArray *result = [NSMutableArray array];
if (array1.count > 0) {
[result addObject:array1];
}
if (array2.count >0) {
[result addObject:array2];
}
return result;
}
Which allows for very simple number of rows/sections:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return self.sourceArray.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[self.sourceArray objectAtIndex:section] count];
}
Standard Cell/Header formatting:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.textLabel.text = [[self.sourceArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [NSString stringWithFormat:#"Section %i", section];
}
This is where I do the magic
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
I notice that the row that comes from the to-be-deleted section is the one that disappears until you retouch the order control.
I suspect that when this datasource method is called by the tableview, its state is still in the middle of performing the move, so calling 'deleteSections' will make the table try and delete the row you're moving. It's not so much of a merge as the fact that it's fading away at the same rate as the section header, and the one below it is just scooting back up to fill the space.
Tapping the control causes the table view to rejigger itself and realize that the row isn't actually gone.
to try and work around this, try running the deletion in the next runloop, via a dispatch call, like:
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
dispatch_async(dispatch_get_main_queue(), ^() {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
});
}
this will cause the deletion to run on the main thread still, but allow the 'moveRow' and whatever call stack it happens to be in finish up its logic before the deletion call
Your problem is in the animation. One is being done while another is not yet finished (moving & deleting animation) causing one cell to be drawn upon the other. You can verify this by moving the cells around again. The correct order will then be displayed. According to Apple's docs on the UITableView:
Note: The data source should not call setEditing:animated: from within its implementation of tableView:commitEditingStyle:forRowAtIndexPath:. If for some reason it must, it should invoke it after a delay by using the performSelector:withObject:afterDelay: method.
Therefore to fix this, do this to your code:
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self performSelector:#selector(someMethod:) withObject:fromIndexPath afterDelay:1.0];
}
- (void) someMethod:(NSIndexPath *) fromIndexPath {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
Should work fine. Just change the delay to something shorter that suites you.
On the off chance that your rows or what's inside them can take focus, have you checked that you have called resignFirstResponder or [view endEditing:YES]? We saw this when we used text fields and (IIRC it was iOS 4 version dependent too) left the focus in one of the fields.
You have to reload the tableview after deleting the section. Try this code.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
}
}
Swap the order of fromArray and toArray in your code. If the item has a retain count of 1 prior to removing it from the array, it will have a retain count of 0 before adding it to toArray.
If you swap the order, the item will go from retain count of 1 to 2 then back to 1 when the remove is complete.
I think the UITableViewRowAnimationFade animation is interfering with the UITableViewCell move animation. One thing you can try is to delay the section deletion a little bit late in order for the cell move row animation to finish.
Try replace your code with the following code.
-(void)deleteSection:(NSIndexSet*)indexSet
{
[self.tableView deleteSections:indexSet withRowAnimation:UITableViewRowAnimationFade];
}
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray *fromArray = [self.sourceArray objectAtIndex:fromIndexPath.section];
NSMutableArray *toArray = [self.sourceArray objectAtIndex:toIndexPath.section];
NSString *movedObject = [[self.sourceArray objectAtIndex:fromIndexPath.section] objectAtIndex:fromIndexPath.row];
[fromArray removeObject:movedObject];
[toArray insertObject:movedObject atIndex:toIndexPath.row];
if ([self.tableView numberOfRowsInSection: fromIndexPath.section] == 0) {
[self performSelector:#selector(deleteSection:) withObject:[NSIndexSet indexSetWithIndex:fromIndexPath.section] afterDelay:1.0];
// [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:fromIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
a solution that lost animation on last row :
if([listOfItemsOnTransaction count]==indexPath.row){
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
}else
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil]
withRowAnimation:UITableViewRowAnimationFade];
}

RowAnimation looks weird with different height cells

I have a problem with animating deletes and inserts on a UITableView. The Fact ist, that I have different cell heights, some 44.0 some 135.0 now. I have uitableviewstylegrouped with different sections. The first row of each sections is a grouping row. On click I remove all rows of this section except the grouping row. But that animation looks weird, when animating (UITableViewRowAnimationTop) the 135px height cell. I tried to set self.tableview.cellheight to 135 and commented out the tableView:cellHeightForIndexPath-Method. And the Animation works fine. Or I set every cellheight to 135 in tableView:cellHeightForIndexPath-Method.
It looks like the animation process checks the height of the first row in the sections an takes that height of the cell for all following cells to animate.
Somebody has an idea?
- (void) showhideGroup:(int)group
{
NSMutableArray *indexPaths = [[NSMutableArray alloc] init];
MCGroup *handleGroup = [groups objectAtIndex:group];
NSMutableArray *groupRows = [visibleGroupData objectForKey:handleGroup.title];
[self.tableView beginUpdates];
if (!handleGroup.hidden) {
int row = 0;
for(MobileFieldMapping *field in groupRows)
{
if (![field.datatype matchesInsensitive:GROUPING_CELL])
{
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:group];
[indexPaths addObject:path];
}
row++;
}
row = 0;
for(NSIndexPath *index in indexPaths)
{
[groupRows removeObjectAtIndex:index.row-row];
row++;
}
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
handleGroup.hidden=YES;
}
else
{
NSMutableArray *allGroupRows = [groupData objectForKey:handleGroup.title];
int row = 0;
for (MobileFieldMapping *field in allGroupRows)
{
if (![groupRows containsObject:field])
{
NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:group];
[indexPaths addObject:path];
[groupRows insertObject:field atIndex:row];
}
row++;
}
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
handleGroup.hidden=NO;
}
[self.tableView endUpdates];
[indexPaths release];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[FormCell class]])
{
return [(FormCell*)cell height];
}
return 44.0;
}
I had the same problem.
My workaround is to use fade section animation:
[tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
Instead of one for elements:
[tableView deleteRowsAtIndexPaths:tmpArray withRowAnimation:UITableViewRowAnimationTop];
This looks much smoother.
An old one that is still unanswered. I assume you figured it out a long time ago, but here is my first thought. (I recall trying to do something like this myself.)
When the UITableView calls heightForRowAtIndexPath:, it will be because the table is trying to prepare the cell, so you can't use the cell to return the height. This may cause an infinite regression in calls or it may detect this and just give up or throw an exception and leave you holding the bag.
You must calculate the cell height without using the cell itself.
As for the cell height assumption, try calling insert/delete for individual cells rather than a single call to do it all at once. The UITableView will still batch them up for the animation after you call endUpdates.

UITableView : crash when adding a section footer view in empty section

This is the first time I ask a question here, but I have to say this site has been a tremendous help for me over the last couple months (iphone-dev-wise), and I thank you for that.
However, I didn't find any solution for this problem I'm having:
I have a UITableView with 2 sections, and no rows when the app is launched for the first time. The user can fill the sections later on as he wishes (the content is not relevant here). The UITableView looks good when filled with rows, but looks pretty ugly when there is none (the 2 header sections are stuck together with no white space in between). That is why I'd like to add a nice "No row" view in between when there is no row.
I used viewForFooterInSection:
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
if(section == 0)
{
if([firstSectionArray count] == 0)
return 44;
else
return 0;
}
return 0;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
if(section == 0)
{
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(200, 10, 50, 44)];
label.backgroundColor = [UIColor clearColor];
label.textColor = [UIColor colorWithWhite:0.6 alpha:1.0];
label.textAlignment = UITextAlignmentCenter;
label.lineBreakMode = UILineBreakModeWordWrap;
label.numberOfLines = 0;
label.text = #"No row";
return [label autorelease];
}
return nil;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section == 0)
{
return [firstSectionArray count];
}
return [secondSectionArray count];
}
This works great : the footer view appears only when there is no row in section 0.
But my app crashes when I enter edit mode and delete the last row in section 0:
Assertion failure in -[UIViewAnimation initWithView:indexPath:endRect:endAlpha:startFraction:endFraction:curve:animateFromCurrentPosition:shouldDeleteAfterAnimation:editing:]
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Cell animation stop fraction must be greater than start fraction'
This does not happen when there are several rows in section 0. It only happens when there is only one row left.
Here's the code for edit mode:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// If row is deleted, remove it from the list.
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Find the book at the deleted row, and remove from application delegate's array.
if(indexPath.section == 0)
{ [firstSectionArray removeObjectAtIndex:indexPath.row]; }
else
{ [secondSectionArray removeObjectAtIndex:indexPath.row]; }
// Animate the deletion from the table.
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationBottom];
[tableView reloadData];
}
}
Does anyone have any idea why this is happening?
Thanks
It looks like what it happening is that internally Apple's UITableView code is assuming that when you delete a row, the first section of your view will get shorter. By making the section header footer suddenly get taller to compensate for the last row, you appear to be confusing it.
Two ideas for you to try:
1. try making the optional section footer a pixel or two smaller than the table cell that's going away, so that the animation code gets to do a pixel or two of animation
2. instead of deleting the only row of your table, when there are no "real" data rows let the table still have numberOfRowsInSection return 1 and make a fake "no data" cell rather than using a table footer for the "no data" case.
This is one of those cases where you just have to accept that Apple has written half of your program for you and you have to conform to some choices your co-author has made, whether you like them or not.
make empty section
use table header in this section instead of footer in first section
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
if (section != OTHER_SECTION_INDEX) {
return nil;
}
//your code here
}
- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (section != OTHER_SECTION_INDEX) {
return 0;
}
//your code
}
it shouldn't be necessary to call both deleteRowsAtIndexPaths:withAnimation: and reloadData on tableView.
the problem you are seeing is the problem i see manifesting in the call to deleteRowsAtIndexPaths:withAnimation:, and thus for you, it's never getting to reloadData anyway.
a simpler solution to having to create new cells, manage them, deal with all of the places where a different cell has to be dealt with, is to use reloadSections:withAnimation: when dealing with a section that is going from 0 to 1 row or 1 to 0 rows.
i.e. replace the following lines of code:
if(indexPath.section == 0)
{ [firstSectionArray removeObjectAtIndex:indexPath.row]; }
else
{ [secondSectionArray removeObjectAtIndex:indexPath.row]; }
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationBottom];
with the following
BOOL useReloadSection;
if (indexPath.section == 0)
{
[firstSectionArray removeObjectAtIndex:indexPath.row];
useReloadSection = 0 == firstSectionArray.count;
}
else
{
[secondSectionArray removeObjectAtIndex:indexPath.row];
useReloadSection = 0 == secondSectionArray.count;
}
if (useReloadSection)
[tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section]
withRowAnimation:UITableViewRowAnimationBottom];
else
[tableView deleteRowsAtIndexPaths:#[indexPath]
withRowAnimation:UITableViewRowAnimationBottom];
I was experiencing the same crash, looks like it's a bug in the OS.
My specific use case pertains to a UITableView with collapsable / expandable sections. I need section dividers (this is what I was using the section footer for) but not row dividers (can't simply use the cell separator).
I wasn't able to resolve the issue by adjusting the height of the section footer. But switching to using a section header did the trick, the bug/crash is no longer occurring.
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CGRect frame = (CGRect){0, 0, self.view.bounds.size.width, 1};
UIView *view = [[UIView alloc] initWithFrame:frame];
view.backgroundColor = [UIColor commentReplyBackground];
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 1.0f;
}
For those of you who still need the section footer, you can also try changing the TableView style to UITableViewStyleGrouped. This seems to bypass the issue. In order to get the same visual appearance, you just have to set all your other footers and headers to CGFLOAT_MIN.