iOS UITableView: Hiding separator lines above custom section headers

By July 26, 2013Mobile, Xamarin

I wanted to share a little trick I discovered when creating custom section headers for a UITable. The issue is that UIKit renders in an undesirable way (in my opinion) when using the combination of default separator lines (between table cells) and custom section header views. The examples shown here are in C# using Xamarin.iOS, but the same technique applies if you are using Objective-C.

Before we look at the issue, let’s take a look at a vanilla UITableView with the standard iOS defined section headers:


I have purposely made the separator lines red so that you can see that, when using the standard UIKit section headers, the separators are not drawn at the top or bottom of the section headers. This looks correct to me.

Now let’s make this table view match the colors of my favorite football team. In order to do this we will need to change the background and text colors, I am assuming you are already familiar with changing the basic properties of table views, and cells. The most involved step is that we will need to supply our own views for the section headers, as these are completely internal to the table view and cannot be customized. This is done by overriding the GetViewForHeader method on the table’s Source and returning a custom UIView. In this case, some simple code for this looks as follows:

01.public override UIView GetViewForHeader(UITableView tableView, int section)
02.{
03.    UIView headerView = new UIView(new RectangleF (0, 0, UIScreen.MainScreen.Bounds.Width, tableView.SectionHeaderHeight));
04.    headerView.BackgroundColor = UIColor.FromRGBA(251, 168, 0, 240);
05. 
06.    // Add Label and any other views you want in your section header.
07.    UILabel sectionTitle = new UILabel( new RectangleF(10, (float)((headerView.Frame.Height - 22) / 2), 200, 22));
08.    sectionTitle.Font = UIFont.BoldSystemFontOfSize(16);
09.    sectionTitle.Text = sections[section];
10. 
11.     
12.    sectionTitle.BackgroundColor = UIColor.Clear;
13.    sectionTitle.TextColor = UIColor.FromRGB(54,16,9);
14. 
15.    headerView.AddSubview(sectionTitle);
16. 
17.    return headerView;
18.}

And the result:

Here you can see the issue, the framework is drawing a separator right above our custom section header view, and it does not look good. This does not happen when Apple provides the section headers. The problem here is that no matter what size we make the custom section header view, the framework will resize it to fit the section height we specify for the table view, and will always draw the additional separator 1 point above it. We can of course roll our own separator lines by providing custom table cells, coding the logic to figure out when to draw a separator at the bottom of a cell, and using these custom cells in our table, but this is some pretty heavyweight work. Fortunately, there is a small trick we can use to obscure the problematic separator line. The trick is that in iOS, both UIViews and CALayers (a CoreGraphics drawing surface contained in a view) by default do not constrain or clip SubViews or SubLayers that are placed within them. This means that although the framework will resize our headerView, we can place a child UIView or CALayer within this, and size it to extend beyond the bounds of our headerView UIView – thus masking the separator line we do not want. 

Placing another UIView inside of our headerView is the easiest way to go, but I will show code for doing this with a CALayer, so that we can add a bit more polish and easily put a nice gradient in our section header:

01.public override UIView GetViewForHeader(UITableView tableView, int section)
02.{
03.    UIView headerView = new UIView(new RectangleF (0, 0, UIScreen.MainScreen.Bounds.Width, tableView.SectionHeaderHeight));
04. 
05.    // Using a CAGradient layer for easy gradient drawing
06.    CAGradientLayer gradient = new CAGradientLayer();
07.     
08.    // The magic is here. Specifying a Y coord of -1 will allow
09.    // the gradient to extend beyong the headerView and
10.    // therefore cover the separator drawn by UIKit.
11.    gradient.Frame = new RectangleF(0,-1,headerView.Bounds.Size.Width,headerView.Bounds.Size.Height+1);
12.     
13.    // Setup the gradient
14.    CGColor[] gradientColors = {UIColor.FromRGBA(251,168,0,240).CGColor,UIColor.FromRGBA(129,88,0,255).CGColor};
15.    gradient.Colors = gradientColors;
16.     
17.    // Now add the layer as a sublayer of the
18.    // headerView layer.
19.    headerView.Layer.InsertSublayer(gradient,0);
20. 
21.    // Add Label and any other views you want in your section header.
22.    UILabel sectionTitle = new UILabel( new RectangleF(10, (float)((headerView.Frame.Height - 22) / 2), 200, 22));
23.    sectionTitle.Font = UIFont.BoldSystemFontOfSize(16);
24.    sectionTitle.Text = sections[section];
25. 
26.    sectionTitle.BackgroundColor = UIColor.Clear;
27.    sectionTitle.TextColor = UIColor.FromRGB(54,16,9);
28. 
29.    headerView.AddSubview(sectionTitle);
30. 
31.    return headerView;
32.}

As mentioned in the comments, the magic is in Line 11, where we shift the Y coordinate up by one so that the header gradient will be drawn out of the bounds enforced by UITableView rendering and will cover the separator line drawn by UIKit. The end result looks as follows:

The following two tabs change content below.
  • Brennan

    Thanks for the info. I am using this in an application and as you mention, this seems to be the simplest way to avoid the intrinsic problem iOS has with cell dividers. Why they won’t let us specify whether we want a row/cell divider on a per cell or per prototype cell basis, I’ll never know, but until that happens, I’ll use this technique.

  • Tobias Schuster

    Since there is still no solution from Apple, this is still the best way to do it. Thank you very much!