UITableView节标题和节页脚不更新(重绘问题)

问题描述:

更新4.0

似乎iOS 4.0在这里改变了一些东西。根据我的第一次快速检查,在描述的场景中为部分标题生成不正确背景的相同代码正在使用4.0!

Seems like iOS 4.0 changed something here. Same code producing incorrect backgrounds for section header in the described scenario is working with 4.0 according to my first quick check!

原始

我有一个UITableView分组样式,带有自定义页眉和页脚视图。在页脚里面我放了一个UILabel和一个UIButton。

I have a UITableView grouped style with custom header and footer view. Inside the footer I put a UILabel and a UIButton.

点击按钮隐藏或显示一些行,更新页脚视图中的UILabel,最后调整页脚视图的大小。

Clicking on the button hides or show some rows, updates the UILabel in the footer view and finally resizes footer view.

基本上一切正常。但是文本离开标签没有在屏幕上更新。它在UILabel文本属性中更新,但只有当我将部分页脚滚出可见区域并向后滚动时,它才会更新。所以这是UITableView的典型重绘问题。

Basically everything is working fine. BUT the text ion the label is not updated on the screen. It is updated in the UILabel text property, but only if I scroll the section footer out of the visible area and scroll it back, it is updated. So it's a typical redraw problem here of the UITableView.

我尝试了每种方法来强制更新,例如needsLayout等。没有任何帮助。

I tried every method to force update like needsLayout etc. Nothing helped.

我看过一些相关的问题,但有一些不同的行为,没有解决方案。任何帮助/想法?

I have seen some related questions but with some different behaviour and no solution. Any help/ideas?

谢谢,Gerd

更新:

我的问题出现在页脚,所以这是我的viewForFooterInSection。

My problems occurs with section footer, so here is my viewForFooterInSection.

基本上我想折叠/展开一个部分,但不是完全(这是一件容易的事)而只是空单元格(ItemSize为空)。如果它是折叠的,则footerView很大,如果它被展开则会缩小。此外,标签文本将更改。

Basically I want to collapse/expand a section, but not completely (that was an easy thing) instead only the empty cell (ItemSize empty). The footerView is large if it is collapsed and will shrink if it is expanded. Furthermore the label text will change.

- (UIView *)tableView: (UITableView *)tableView viewForFooterInSection: (NSInteger)section{
NSLog(@"viewForFooterInSection section:%i", section);

UIButton *myView;
UILabel *label;

if ([[[self.sectionStatus objectAtIndex:section] valueForKey:@"collapseStatus"] isEqual:@"collapse"]){ 
    myView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, 52)];
    [myView setBackgroundImage:[UIImage imageNamed:@"ItemViewFooter.png"] forState:UIControlStateNormal];
    label = [[UILabel alloc] initWithFrame:CGRectMake(20, 32, 300, 20)];
    label.text = NSLocalizedString(@"list_expand",@"");
} else { //is expanded
    myView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 320, 21)];
    [myView setBackgroundImage:[UIImage imageNamed:@"ListCollapseExpand.png"] forState:UIControlStateNormal];
    label = [[UILabel alloc] initWithFrame:CGRectMake(20, 1, 300, 20)];
    label.text = NSLocalizedString(@"list_collapse",@"");
}

myView.tag=section;
[myView addTarget:self action:@selector(collapseExpandAction:) forControlEvents:UIControlEventTouchUpInside];
myView.backgroundColor = [UIColor clearColor];
myView.adjustsImageWhenHighlighted = NO;
myView.showsTouchWhenHighlighted = YES;

label.textColor = FONTCOLOR;
label.font = [UIFont systemFontOfSize:14];
label.numberOfLines = 1;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[myView addSubview:label];
return myView;

};

在按钮操作中方法I存储节折叠/展开的状态和显示的行数。比我删除或插入行。 (必须使用插入/删除,因为我需要动画)。

In the button action method I store status of section collapse/expand and the number of displayed rows. Than I delete or insert rows. (It has to be with insert/delete because I need the animation).

- (void) collapseExpandSection: (NSInteger) section{
NSMutableArray *paths = [NSMutableArray arrayWithCapacity:10];
NSInteger row;
NSInteger numberOfDisplayedItems=[[[self.sectionStatus objectAtIndex:section] valueForKey:@"numberOfDisplayedRows"] intValue];
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
NSInteger numberOfAllItems=[sectionInfo numberOfObjects];
Item *tmpItem=nil;
NSSet *itemsWithSizes=nil;

//filter not used cells
for ( row = 0; row < numberOfAllItems; row++ ) {
    tmpItem=[fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:row inSection:section]];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"itemSize != nil"];
    NSSet *itemsWithSizes = [tmpItem.itemSizes filteredSetUsingPredicate:predicate];
    if ([itemsWithSizes count]==0){ 
        [paths addObject:[NSIndexPath indexPathForRow:row inSection:section]]; //all unused cells
    };
}

if (numberOfDisplayedItems == numberOfAllItems){ //currently all shown => Collapse
    [self.tableView beginUpdates];
    [[self.sectionStatus objectAtIndex:section] setValue:[NSNumber numberWithInt:(numberOfDisplayedItems-[paths count])] forKey:@"numberOfDisplayedRows"];
    [[self.sectionStatus objectAtIndex:section] setValue:@"collapse" forKey:@"collapseStatus"];
    [self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView endUpdates];
} else { //Not all shown so expand with the unused cells
    [[self.sectionStatus objectAtIndex:section] setValue:[NSNumber numberWithInt:(numberOfDisplayedItems+[paths count])] forKey:@"numberOfDisplayedRows"];
    [[self.sectionStatus objectAtIndex:section] setValue:@"expand" forKey:@"collapseStatus"];
    [self.tableView beginUpdates];
    [self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView endUpdates];
}
return;

};

完成所有这些工作总的来说很好。块开始/结束更新后,将为每个部分调用viewForFooter,并在属性中设置正确的标签文本。但是显示无法正确更新。一旦强制重新显示(滚动滚动),显示就可以了。

Doing all this works fine in general. After the blocks begin/endupdate the viewForFooter is called for every section and the label text is set correct in the property. However the display doesn't update correctly. As soon as a redisplay is forced (srolling out- scrolling in) the display is OK.

有2个问题。 >
第一个问题是部分页脚没有更新。
更新后尝试调用 [tableView reloadData] [tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationFade] 可能与dalay)。

第二个问题是 myView 标签中的内存泄漏。
当你可以使用按钮的内部标签时,为什么还要使用标签?
P.S.不要直接分配 UIButton 对象,因为它是工厂。改为调用 [UIButton buttonWithType:UIButtonTypeCustom]

There 2 problems.
First problem is that section footer not updated. Try call [tableView reloadData] or [tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationFade] after your update (may be with dalay).
Second problem is memory leaks in myView and label. Also why do you use label when you can use button's internal label? P.S. Don't allocate UIButton object directly because it is factory. Call [UIButton buttonWithType:UIButtonTypeCustom] instead.

更新:另一种更新方式是通过访问页脚视图直接更新页脚。

Upd: Another way to update is to update footer directly by accessing footer views.

- (void) collapseExpandSection: (NSInteger) section{

检查该部分是否真的是你的按钮

Check that section is actualy your button

 - (void) collapseExpandSection: (UIButton*) sender{
// Do update of sender here
// Do other stuff
}

此外,你可以尝试下一个技巧:在委托中创建UIView对象,在其上添加你的按钮和标签,然后返回对象视图本身的实例。

Also you can try next trick: create UIView object in delegate, add your button and label on it and return instaed of buttom view itself.