UITableView: hide header from empty section

Objective CIosUitableviewTableview

Objective C Problem Overview


i have a UITableView, that displays expenses from a current month (see screenshot):

My problem is with the header for empty sections. is there any way to hide them? The data is loaded from coredata.

this is the code that generates the header title:

TitleForHeader

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
    return nil;
} else {

NSDate *today = [NSDate date ];
int todayInt = [dataHandler getDayNumber:today].intValue;

NSDate *date = [NSDate dateWithTimeIntervalSinceNow:(-(todayInt-section-1)*60*60*24)];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale preferredLanguages] objectAtIndex:0]]];    
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSString *formattedDateString = [dateFormatter stringFromDate:date];
    return formattedDateString;}

}

ViewForHeader

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
    return nil;
} else {

    UIView *headerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 312, 30)];
    UILabel *title = [[UILabel alloc]initWithFrame:CGRectMake(4, 9, 312, 20)];
    UIView *top = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 312, 5)];
    UIView *bottom = [[UIView alloc]initWithFrame:CGRectMake(0, 5, 312, 1)];
    
    [top setBackgroundColor:[UIColor lightGrayColor]];
    [bottom setBackgroundColor:[UIColor lightGrayColor]];

    [title setText:[expenseTable.dataSource tableView:tableView titleForHeaderInSection:section]];
    [title setTextColor:[UIColor darkGrayColor]];
    UIFont *fontName = [UIFont fontWithName:@"Cochin-Bold" size:15.0];
    [title setFont:fontName];
    
    
    [headerView addSubview:title];
    [headerView addSubview:top];
    [headerView addSubview:bottom];
    
    return headerView;

}

}

heightForHeader

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {

NSLog(@"Height: %d",[tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0);
if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section == 0]) {
    return 0;
} else {
    return 30;
}
}

numberOfRowsInSection

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
 {

int rows = 0;
for (Expense* exp in [dataHandler allMonthExpenses]) {
    if ([exp day].intValue == section) {
        rows++;
    }
}

return rows;
}

enter image description here sebastian

Objective C Solutions


Solution 1 - Objective C

You have to set tableView:heightForHeaderInSection: to 0 for the appropriate sections. This is something which changed fairly recently and got me in a couple places. From UITableViewDelegate it says...

> Prior to iOS 5.0, table views would automatically resize the heights > of headers to 0 for sections where tableView:viewForHeaderInSection: > returned a nil view. In iOS 5.0 and later, you must return the actual > height for each section header in this method.

So you'll have to do something like

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return 0;
    } else {
        // whatever height you'd want for a real section header
    }
}

Solution 2 - Objective C

What if in – tableView:viewForHeaderInSection: you return nil if the section count is 0.

EDIT : You can use numberOfRowsInSection for obtaining the number of elements in the section.

EDIT: Probably you should return nil also in titleForHeaderInSection if numberOfRowsInSection is 0.

EDIT: Did you implement the following method?

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

EDIT : Swift 3 example

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    switch section {
    case 0:
        if self.tableView(tableView, numberOfRowsInSection: section) > 0 {
            return "Title example for section 1"
        }
    case 1:
        if self.tableView(tableView, numberOfRowsInSection: section) > 0 {
            return "Title example for section 2"
        }
    default:
        return nil // when return nil no header will be shown
    }
    return nil
}

Solution 3 - Objective C

In my strange situation I have to return:

> viewForHeaderInSection -> nil > >  viewForFooterInSection -> nil (don't forget about footer!) > > heightForHeaderInSection -> 0.01 (not zero!) > >  heightForFooterInSection -> 0.01

only in this case empty sections disappear completely

Solution 4 - Objective C

Take a look at the method -[UITableViewDelegate tableView:heightForHeaderInSection:]. Especially the note that accompanies its documentation:

> Prior to iOS 5.0, table views would automatically resize the heights > of headers to 0 for sections where tableView:viewForHeaderInSection: > returned a nil view. In iOS 5.0 and later, you must return the actual > height for each section header in this method.

Solution 5 - Objective C

I know this is an old question but I'd like to add to it. I prefer the approach of setting the titleHeader to nil over altering the heightForHeaderInSection to 0 as it can cause problems with indexPath being +1 from where is should be due to the header still being there but hidden.

So with that said and building on DBD's answer you can set the titleForHeaderInSection: to nil for sections with no rows in it like so:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    } else {
        // return your normal return
    }
}

Solution 6 - Objective C

In 2015 using iOS 8 and Xcode 6, the following worked for me:

/* Return the title for each section if and only if the row count for each section is not 0. */

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{

    if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
        return nil;
    }else{

    // here you want to return the title or whatever string you want to return for the section you want to display

    return (SomeObject*)someobjectArray[section].title;
    }
}

Solution 7 - Objective C

This seems to be the proper way, It will animate correctly & works clean... as Apple intended...

Provide appropriate info to the tableView delegate

When no items in section, Return 0.0f in:

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section

..Also return nil for:

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

Do appropriate data removal for tableView

  1. Call [tableView beginUpdates];
  2. Remove items from your dataSource, keeping track of where elements got removed..
  3. Call deleteRowsAtIndexPaths with the indexPaths of the cells you removed.
  4. if your datasource has no items in it (Here you would end up with just the header). Call reloadSections: to reload that section. This will trigger the correct animation and hide/slide/fade the header.
  5. Finally call [tableView endUpdates]; to finish the update..

Solution 8 - Objective C

Swift 4.2

Set the heightForHeaderInSection to Zero and if you've a custom section view, set it to nil for sections without cells.

func tableView(_ tableView: UITableView,
                   heightForHeaderInSection section: Int) -> CGFloat {
        return height_DefaultSection
    }
    
func tableView(_ tableView: UITableView,
                   viewForHeaderInSection section: Int) -> UIView? {
        
        return tableView.dataSource?.tableView(tableView, numberOfRowsInSection: section) == 0 ? nil: headerView(tableView: tableView, section: section)
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSebastian FlückigerView Question on Stackoverflow
Solution 1 - Objective CDBDView Answer on Stackoverflow
Solution 2 - Objective CLuisEspinozaView Answer on Stackoverflow
Solution 3 - Objective CdjdanceView Answer on Stackoverflow
Solution 4 - Objective CSixten OttoView Answer on Stackoverflow
Solution 5 - Objective CgavView Answer on Stackoverflow
Solution 6 - Objective CRonaldoh1View Answer on Stackoverflow
Solution 7 - Objective CAndres CanellaView Answer on Stackoverflow
Solution 8 - Objective Cuser550088View Answer on Stackoverflow