My first post is about an annoying little obstacle apple put in front of iOS developers, when trying to disable the floating header behavior of UITableViewStylePlain. I’ll demonstrate the common approaches when trying to change the default behavior of a sealed apple class.
For all you lazy readers, the answer to the question in the title is only one line long and you can just skip and copy-paste it from the end.
All you curious, and probably better, developers – keep on reading, we’ll get there eventually.
What are floating headers
Without a doubt the most useful component to an iOS developer is the UITableView. It’s a very whole UI component, which almost every app uses at least once, the problems start when you try to change its default behavior. One of those default behaviors is the “Floating section headers”. UITableView has two primary styles - UITableViewStylePlain and UITableViewStyleGrouped. When using the UITableViewStylePlain style the top section header remains “glued” to the top when we’re scrolling.
In my opinion, this is a very good looking feature, and most of the times it’s exactly what I need.
However in one of my apps I needed to disable this feature. Surprisingly, after some heavy googling, I learned that there is no easy way to accomplish this..
There were two solutions, representing two of the three most common ways to deal with scenarios where you want to change the default behavior of an apple controller. Those solutions are:
Using private APIs
Since the “floating headers” behavior is so focused, you would assume that there should be a simple flag we can turn on or off for this feature. Annoyingly enough, there is one, but it’s a private API. If you’re new to iOS development than you are going to encounter a whole bunch of useful private APIs that apple uses in their native applications that we don’t have access to.
It can make sense sometimes – when they’re testing new features or want a solid UI line, but sometimes it seems just plain mean. The UITableView private APIs apple is hiding from us are:
- (BOOL) allowsHeaderViewsToFloat;
- (BOOL) allowsFooterViewsToFloat;
Subclassing UITableView and overriding those methods allow us to disable the floating section headers for the plain style. Very simple, and this is actually how UITableViewStyleGrouped does it. The only problem is that using private APIs will get your app automatically rejected from the app store! As tempting as it can be using some of the hundreds of useful private APIs out there, if we want our app to be approved we can never use them.
Ok, so what Can we do? The second suggested solution on stack overflow was to take the second most common approach when changing apple controllers:
Re implementing parts of the controller
A lot of times we have to make small adjustments to apple controls such as UITableView. Most of the time we can use the allowed API but in a “hacked” way, to achieve our goal. These solutions usually require a little more profound understanding of how the controller works, and though they seem patch-like, they are using only public APIs are will not get you rejected from the app store.
The suggested solution was to implement section headers as custom UITableViewCells.
This is quite a good solution. Most of the times we’ll want to use our own design for the sections anyway, so we just need to use the custom design as a tableViewCell instead of as a custom UIView with viewForHeaderInSection. Besides the UI, all we need to do is manage ourselves a mapping between the section and its cells, instead of letting UITableView handle it for us.
Ok, so this will work. But hey, I promised you three common approaches. (Also a one-line solution, but this will have to wait). So, the third, and most complicated approach:
Building a custom control ourselves
There are times, where there is simply no easy way to “bend” the apple component to behave as we want it too. This can be because the component is too sealed, or we just want too big a change.
In those cases, our only option is to build our own component that behaves just like the original one.
This is hardest solution, but it’s also the most flexible one, and it allows us complete control over everything we need to change.
The internet is filled with open source components that simulate specific apple-components. A good place to start is a great site called Cocoa Controls. Before you start implementing your own control, search this site. Most likely you will find there something close enough.
The apple-control we want to recreate here is the UITableView. For this specific control, the best advice I can give you is: Don’t ever try to build your own UITableView!!. This is the most complicated and sealed apple control, and no matter how much you think you need to build it from scratch, it’s never the solution. UITableView has such a rich and complicated logic that apple changes and adjusts heavily on iOS updates, that in 100% of the cases, it’s better off to compromise a little with your UI request than implement UITableView your self.
Don’t worry, there are enough apple controls that we can build from scratch, in one of the following posts I’ll show you how to build UIPickerView, which is a very non customizable control, with quite a basic logic.
Well, these were the most common ways to handle customizing a sealed apple control.
And now, as I promised:
A one-line solution to disable floating headers
While thinking how to approach this problem, I remembered a very important detail about UITableViewStyleGrouped.
The way UITableView implements the grouped style (the rounded borders around the cells) is by adding a custom backgroundView to the UITableViewCells, and not to the UITableView. Each cell is added a backgroundView according to its position in the section (upper rows get the upper part of the section border, middle ones get the side border and the bottom one gets – well, the bottom part).
So, if we just want a plain style, and we don’t have a custom backgroundView for our cells (which is the case in 90% of the times), then all we need to do is use UITableViewStyleGrouped, and remove the custom background. This can be done by following those two steps:
- Change our tableView style to UITableViewStyleGrouped
- Add the following line to cellForRow, just before we return the cell:
cell.backgroundView=[[[UIView alloc] initWithFrame:cell.bounds] autorelease];
And that’s it. The tableView style will become exactly like UITableViewStylePlain, except for the floating headers.
I hope you enjoyed the article, and i’d love to hear your comments.