UITableView header and section footer not updating (redrawing issue)

UPDATE 4.0

It seems like iOS 4.0 has changed something. The same code creating the wrong backgrounds for the section title in the described scenario works with 4.0 as per my first quick test!

Original

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

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

Basically everything works fine. BUT the text icon doesn't update on the screen. It updates in the UILabel text property, but only if I scroll the section footer out of the visible area and scroll back it is updated. So this is a typical redrawing issue here for UITableView.

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

I've seen some related questions, but with some different behavior and no solution. Any help / ideas?

Thanks, Gerd

UPDATE:

My problem is with the section footer, so here is my viewForFooterInSection.

Basically, I want to collapse / expand a section, but not completely (it was easy), but only an empty cell (ItemSize empty). FooterView is big if collapsed and will shrink if expanded. In addition, 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;

      

};

In the button action method, I store the break / unroll state of the section and the number of rows displayed. Than I delete or insert lines. (It should be with insert / delete because I need 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;

      

};

Doing all of this works great overall. After the blocks start / end, the viewForFooter is called for each section and the label text is set correctly in the property. However, the display is not updating correctly. Once forced to redraw (scroll scroll) the display is fine.

+2


source to share


3 answers


There are 2 problems.
The first problem is that the footer is not updating. Try calling [tableView reloadData]

or [tableView reloadSections:sections withRowAnimation:UITableViewRowAnimationFade]

after your update (with dalay maybe).
The second problem is memory leak in myView

and label

. Also why are you using a shortcut when you can use an internal button label? Postscript Don't allocate an object UIButton

directly because it's a factory. Call [UIButton buttonWithType:UIButtonTypeCustom]

instead.

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

- (void) collapseExpandSection: (NSInteger) section{

      



Check if your button is valid

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

      

You can also try the following trick: create a UIView object in the delete, add your button and label to it, and return an instance of the view itself.

+4


source


My problem was that I expanded the section title to display the search bar, but it won't redraw the view until I scroll the UITableView.

I had my own SectionHeader class that subclassed the UIView and controlled the search.

After my animation, I just used this to force the update. It's not pretty, but it works.



CGPoint point = CGPointMake(((UIScrollView *)self.superview).contentOffset.x,
                            ((UIScrollView *)self.superview).contentOffset.y+1);
[((UIScrollView *)self.superview) setContentOffset:point animated:NO];

point = CGPointMake(((UIScrollView *)self.superview).contentOffset.x, 
                    ((UIScrollView *)self.superview).contentOffset.y-1);
[((UIScrollView *)self.superview) setContentOffset:point animated:NO];

      

Basically force the UITableView to scroll down 1 pixel and 1 pixel.

+3


source


I got this problem when I wanted to update the section title after inserting a new row. I found that calling the method tableView reloadSections()

with the animation setting .None

after calling the insertRows method worked for me (both calls in the same tableView update block). I got the insert animation I wanted and also the section title was updated.

0


source







All Articles