81

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

Sebastian Flückiger
  • 5,525
  • 8
  • 33
  • 69

8 Answers8

138

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
    }
}
lmirosevic
  • 15,787
  • 13
  • 70
  • 116
DBD
  • 23,075
  • 12
  • 60
  • 84
  • sadly it does not work.. i updated the question with new code & a new screenshot. – Sebastian Flückiger Mar 16 '12 at 15:45
  • 3
    Static tables are a bit different. This code is for custom table header views, not just a `title`. If you are setting a `title` you will need to return `nil` for the method call `tableView:titleForHeaderInSection:` when appropriate. If you are using custom header views you must be overriding `viewForHeaderInSection`. If you are doing that, this code should work. If not, I would need more information to help with the issue. – DBD May 13 '13 at 13:33
  • awesome... thanks a lot. earlier i was returning a blank `UIView` like this: `else` `{` `vw = [[UIView alloc]init];` `[vw setHidden:YES];` `}` – Vaibhav Saran May 15 '13 at 06:32
  • i used viewforheader in section + above code and worked perfectely in ios 6 & even in lower version. thnnxx buddyy – Amrit Trivedi May 16 '13 at 13:28
  • Worked for me - I am on ios6 – Shirish Kumar Jul 09 '13 at 04:51
  • It seems that if your table view is the first child of a navigation view, there's a bug: http://stackoverflow.com/a/20517881/3279 – Ben Collins Jan 30 '14 at 03:02
  • OK, but can someone explain why this doesn't work then? `[tableView numberOfRowsInSection:section] == 0` – turingtested Dec 18 '15 at 13:11
  • @turingtested did you verify that you're returning nil in `tableView:titleForHeaderInSection:`? – Liron Yahdav Jun 20 '16 at 22:30
58

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
}
mourodrigo
  • 2,232
  • 28
  • 18
LuisEspinoza
  • 8,508
  • 6
  • 35
  • 57
  • sadly it does not work.. i updated the question with new code & a new screenshot. – Sebastian Flückiger Mar 16 '12 at 15:45
  • no error. just finished editing the question. now the new code is in and the new screenshot. – Sebastian Flückiger Mar 16 '12 at 15:48
  • by the way, did you implement numberOfRowsInSection? – LuisEspinoza Mar 16 '12 at 15:49
  • yes of course - otherwise i would not have any filled sections =) but for the sake of completion i added the function as well =) – Sebastian Flückiger Mar 16 '12 at 15:56
  • every time that you do a comparation, you close with .... ==0]), is that ok?...i will use ...]==0) – LuisEspinoza Mar 16 '12 at 15:59
  • damn - sometimes i guess its best to stop working after some time ^^ that was of course an error, but it shifts the problem and doesnt solve it. now it makes a destinction between empty & filled sections. but the header is not gone. it now uses the standard header for zero-sections. (screenshot added in question). – Sebastian Flückiger Mar 16 '12 at 16:07
  • added the updated code & a new screenshot. now i have the default header with a nil titlelable but the height doesnt go to 0 :/ – Sebastian Flückiger Mar 16 '12 at 16:11
  • 1
    and what if you check the size of the section header in the nib?..maybe if you use 0 you get a better result..since you are setting it in code. – LuisEspinoza Mar 16 '12 at 16:12
  • YES! it works :D you are my hero. found a last faulty comparison. thanks a million times for fighting through my long question =) – Sebastian Flückiger Mar 16 '12 at 16:12
  • FYI: What makes this scary for me is that adding this line will break things: `- (float)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{return [super tableView:tableView heightForHeaderInSection:section];}` this is the sign of Apple trying to be too clever – William Entriken May 03 '13 at 01:49
  • 2
    This is an old question now, but `return nil;` in `titleForHeaderInSection` is sufficient for me (after checking `count > 0` for that section of course) – Patrick Aug 01 '14 at 17:38
55

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

djdance
  • 3,110
  • 27
  • 33
  • 10
    I did it just by setting headerHeight to 0.01. – Akshit Zaveri Jan 28 '14 at 09:35
  • 1
    iOS 8.4 I could still see the text using 0.01, but 0.0 worked a charm override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { if sectionedTableData[section].count == 0 { return CGFloat(0.0) } return CGFloat(40.0) } – DogCoffee Aug 08 '15 at 11:55
  • This works for me perfectly. Unfortunately I couldn't found this anywhere else. Thanks dude. – Ruchi Jul 08 '16 at 06:37
  • Even in iOS 12.1, you still have to set the height to ```0.01```.... iOS has some strange bugs. – Supertecnoboff Nov 01 '18 at 16:04
10

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.

Sixten Otto
  • 14,816
  • 3
  • 48
  • 60
9

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
    }
}
Community
  • 1
  • 1
gav
  • 1,293
  • 13
  • 14
7

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;
    }
}
shim
  • 9,289
  • 12
  • 69
  • 108
TheRealRonDez
  • 2,807
  • 2
  • 30
  • 40
5

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..
Andres Canella
  • 3,706
  • 1
  • 35
  • 47
  • 1
    Thanks for the tip on reloadSections: :-) Still cant get anything to work for me with iOS11. Bit annoying :/ – firecall May 21 '18 at 09:37
  • Why do we have to call ```reloadSections```? I noticed that if I don't call it, my table view headers overlap (until I scroll, then the correct header is shown). – Supertecnoboff Nov 01 '18 at 17:36
3

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)
    }
user550088
  • 712
  • 1
  • 8
  • 16