mkale.com

iOS 5 and Blank UITableView section headers

I have an iOS app which uses UITableViews and custom section header views. Some of the sections are intended to be blank, so my -tableView:viewForHeaderInSection looked like this:


- (UIView*)tableView:(UITableView*)tableView viewForHeaderInSection:(NSInteger)section {
    if (section == STATUSVC_SECTION_STATUS) {
        return nil;
    } else {
        return plainHeaderViewWithText(
                 NSLocalizedString(@"Recent Entries", @"..."),
                 tableView.bounds.size.width);
    }
}

where plainHeaderVIewWithText() is a funcion I've implemented like this. (I use it in more than one place).


UIView* plainHeaderViewWithText(NSString *str, CGFloat width) {
    // Note: assumes ARC.  If you're not using it, you'll want
    // to send -autorelease or -release to the UIView and UILabel
    UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, 35)];
    [headerView setBackgroundColor:[UIColor chooseYourColorHere]];
    UILabel *headerText = [[UILabel alloc] initWithFrame:CGRectMake(10, 3, width - 10, 18)];
    headerText.font = [UIFont boldSystemFontOfSize:19.0];
    headerText.shadowColor = [UIColor grayColor];
    headerText.shadowOffset = CGSizeMake(0.0, 1.0);
    headerText.text = str;
    headerText.textColor = [UIColor whiteColor];
    headerText.backgroundColor = [UIColor clearColor];
    [headerView addSubview:headerText];
    return headerView;
}

Starting in iOS 5, though, I started getting unwanted blank headers that looked like this:

Not what I wanted! Looking at the UITableViewDelegate protocol docs, I noticed this bit:

The returned object can be a UILabel or UIImageView object, as well as a custom view. This method only works correctly when tableView:heightForHeaderInSection: is also implemented.

A hah! That must be it. Indeed, the docs for that method mention:

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, the fix is to include this method in my tableview delegate:


- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    if (section == STATUSVC_SECTION_STATUS) {
        return 0.0;
    } else {
        return 22.0;
    }
}

This gets me back where I wanted to be:

Lesson learned: read the docs and implement the methods they tell you to, even if it looks like you don't need 'em!

Update 2012-04-26: converted code above to ARC

Code Prettify

I installed the prettify module from google code so I can write about code here. Let's test it out! Just for fun, here are a few time helper functions that I use:

#include <time.h>

time_t midnightOnDay(time_t t) {
    struct tm theday;
    localtime_r(&t, &theday);
    theday.tm_hour = 0;
    theday.tm_min = 0;
    theday.tm_sec = 0;
    return mktime(&theday);
}

time_t todayMidnight() {
    return midnightOnDay(time(0));
}

bool areSameDay(time_t t1, time_t t2) {
    struct tm tm1, tm2;
    localtime_r(&t1, &tm1);
    localtime_r(&t2, &tm2);
    return ((tm1.tm_mday == tm2.tm_mday) && (tm1.tm_mon == tm2.tm_mon) && (tm1.tm_year == tm2.tm_year));
}

int daysBetween(time_t t1, time_t t2) {
    // we'll be rounding down fractional days, so set t1 to midnight and then do division
    time_t newt1 = midnightOnDay(t1);
    return ((t2 - newt1) / TWENTYFOURHOURS_SECONDS);
}

Let's see how this works!

Update November 4th, 3pm

I disabled the module temporarily while I work out the formatting.

History | Blog | mkale.com