Why the 20px gap at the top of my UIViewController?
Asked 07 September, 2021
Viewed 645 times
  • 59
Votes

The Problem

Consider the following controller hierarchy:

  • UINavigationController
    • UIViewController
      • UITableViewController

The presence of the UIViewController is affecting layout. Without it, the UITableViewController takes up the entire bounds of the UINavigationController:

enter image description here

However, if I add a vanilla UIViewController between the UINavigationController and UITableViewController, a 20px gap appears between the top of the UIViewController and the top of the UITableViewController:

enter image description here

Even if I reduce my code down to the simplest possible thing, I still observe this behavior. Consider this app delegate code:

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    window = new UIWindow(UIScreen.MainScreen.Bounds);

    var tableView = new UITableViewController();
    var intermediateView = new UIViewController();
    var navigation = new UINavigationController(intermediateView);

    navigation.View.BackgroundColor = UIColor.Red;
    intermediateView.View.BackgroundColor = UIColor.Green;
    tableView.View.BackgroundColor = UIColor.Blue;

    intermediateView.AddChildViewController(tableView);
    intermediateView.View.AddSubview(tableView.View);
    tableView.DidMoveToParentViewController(intermediateView);

    window.RootViewController  = navigation;
    window.MakeKeyAndVisible();

    return true;
}

The above still shows a 20px gap between the top of the UIView and the top of the UITableView.

My Understanding of the Problem

I understand that something is erroneously allocating space for a status bar. Using Reveal I can see that the Frame of the UITableViewController has a Y value of 20.

Things I've Tried

Unsuccessful

  • Set WantsFullScreenLayout to true on the UIViewController, UITableViewController, and both
  • Playing with EdgesForExtendedLayout and ExtendedLayoutIncludesOpaqueBars for both the UIViewController and UITableViewController
  • Played with AutomaticallyAdjustsScrollViewInsets on the UIViewController
  • Played with PreservesSuperviewLayoutMargins in the UITableView
  • Tried overriding PrefersStatusBarHidden and returning true in both the UIViewController and UITableViewController

Successful

Overriding ViewDidLayoutSubviews in my UITableViewController thusly:

public override void ViewDidLayoutSubviews()
{
    base.ViewDidLayoutSubviews();
    this.View.Frame = this.View.Superview.Bounds;
}

Things I want to know

  • is there a clean(er) way of achieving my goal?
  • what is actually responsible for adding the 20px gap? The UIViewController? The UITableViewController?
  • what are the best practices for ensuring my view controllers remain usable in different contexts? Presumably overriding ViewDidLayoutSubviews couples my view controller to expectations as to where it will be displayed in the visual tree. If it were to be hosted higher up the controller stack, things would not look right. Is there a way to avoid this coupling and thus increase reusability?

5 Answer