开发者

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开发者_JS百科 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜