Extra Space When Embedding Table Views

If you embed a table view (or other type of scroll view) in a container view that has a navigation bar you will most likely hit what seems to be a strange problem. You get some extra unwanted space between the bottom of the bar and the first row of the table. This is nothing new, it came with iOS 7, and the fix is simple but it can drive you crazy if you don’t realise what is happening.

Mind The Gap - The Problem

Here is the problem. I want to create this layout with a table view and some other view embedded in a parent container view controller that is itself embedded in a navigation controller:

Table View in Container View

Easy enough to add subviews to a view controller in a Storyboard and embed the view controller in a navigation controller:

Storyboard

I have constrained the top of the table view to the bottom of the top layout guide which is the navigation bar:

That already looks odd in the Storyboard. Running the app confirms the problem. We have some extra space between the bar and the first row of the table:

Extra content offset

The Explanation

This is nothing new, the reason dates back to iOS 7 which moved us to full screen views visible when they flowed under a transparent navigation bar. To make this work for scroll views such as UITableView the container view controller automatically adds an extra content inset to the scroll view so that the first row is not hidden by the navigation and status bars.

When you have a UITableViewController with a full screen table view this all works automatically thanks to the automaticallyAdjustsScrollViewInsets property of UIViewController:

automaticallyAdjustsScrollViewInsets

The default value of this property is YES, which lets container view controllers know that they should adjust the scroll view insets of this view controller’s view to account for screen areas consumed by a status bar, search bar, navigation bar, toolbar, or tab bar.

The problem comes when our table view (or other scroll view) is not full screen and does want or need this extra offset.

The Fix

The solution is simple we tell the content view controller to stop messing with our scroll view. Since our table view is not full screen and does not extend under the navigation bar it does not need the extra content offset.

You can fix this in the Storyboard:

Interface Builder

or if you prefer in code:

automaticallyAdjustsScrollViewInsets = false

Just make sure you do it on the parent container view controller not on any embedded child controller.

In this case you can see the change in the Storyboard:

Correct Storyboard

Note that the offset is not always so obvious. Here is the Storyboard I used when writing about Container Views:

Container View Storyboard

In this example the table view is not directly embedded in a navigation controller so you do not see the offset in the Storyboard. This makes it more surprising when you run the App and get some extra spacing. The fix is to set automaticallyAdjustsScrollViewInsets to false on the container view (the view controller on the left). Setting it on the table view controller has no effect. Take a look at the Container Project in my GitHub repository if you want to try for yourself.

Undocumented Behaviour

One final curiosity. The container view controller only seems to look for a scroll view to manage in the first (index zero) subview hierarchy of its view (so any subview of view.subviews[0]). This means that we also fix the problem by reversing the order or our views (without changing the layout so our table view is still at the top under the navigation bar):

Subview order

Now it does not matter how you set automaticallyAdjustsScrollViewInsets you never get the extra space. Safer though to set it to false to make your intentions clear in case you ever change the order of the views.

  • Bug report 31007890 - Improve documentation for automaticallyAdjustsScrollViewInsets