Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the 20px gap at the top of my UIViewController?

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?
like image 274
Kent Boogaart Avatar asked May 29 '15 08:05

Kent Boogaart


People also ask

Why is there extra padding at the top of my UITableView?

Starting in iOS7, there is additional space at the top of my UITableView 's which have a style UITableViewStyleGrouped . The tableview starts at the first arrow, there are 35 pixels of unexplained padding, then the green header is a UIView returned by viewForHeaderInSection (where the section is 0).

What is the difference between viewController and UIViewController?

An UIViewController is just the base class of this already defined "View Controller", you add a viewController as you said, but a viewController has a class associated to it, that can be a UIViewController, UITableViewController, or variations/subclasses.

What is UIViewController life cycle?

The LifecycleThe view controller lifecycle can be divided into two big phases: the view loading and the view lifecycle. The view controller creates its view the first time the view is accessed, loading it with all the data it requires. This process is the view loading.

Which of the following method is called after UIViewController is initialized?

First UIViewController is alloc'ed by some other object, then init is immediately called (or some other init method, like initWithStyle). Only once the object is initialized would I expect it to call its own loadView function, after which the view, once loaded, calls the viewDidLoad delegate method.


1 Answers

The behaviour you are seeing is not a bug at all but merely a side effect of your misuse of adding views into a hierarchy.

When you add the tableView into a view you need to tell UIKit how you want that tableView to size relative to its parent. You have two options: Auto-Layout or Autoresizing Masks. Without describing how you want your view to layout UIKit simply pops it onto the hierarchy and the default implementation will lay your view under the top layout guide (which just happens to be the height of the status bar). Something as simple as this would do the trick:

tableVC.View.Frame = rootVC.View.Bounds tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true// always nice to explicitly use auto layout 

This behavior is not actually exclusive to UITableViewController but also to UICollectionViewController. I believe their default viewLoading implementation insets the view under the status bar. If we make our childController a simple UIViewController subclass none of this behaviour is exhibited. Don't see this as a bug though, if you explicitly declare how you want their respective views to be laid out you won't have this issue. Naturally, this is the primary function of a container controller.

Heres what your appDelegate should look like:

Xamarin

public override bool FinishedLaunching(UIApplication app, NSDictionary options) {     window = new UIWindow(UIScreen.MainScreen.Bounds);      var tableVC = new UITableViewController();     var rootVC = new UIViewController();     var navigationVC = new UINavigationController(intermediateView);      navigationVC.View.BackgroundColor = UIColor.Red;     rootVC.View.BackgroundColor = UIColor.Green;     tableVC.View.BackgroundColor = UIColor.Blue;      rootVC.AddChildViewController(tableVC);     rootVC.View.AddSubview(tableVC.View);       //YOU NEED TO CONFIGURE THE VIEWS FRAME     //If you comment this out you will see the green view under the status bar     tableVC.View.Frame = rootVC.View.Bounds     tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth     tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true     tableVC.DidMoveToParentViewController(rootVC);      window.RootViewController  = navigationVC;     window.MakeKeyAndVisible();      return true; } 

Swift

var window: UIWindow? var navigationControlller: UINavigationController!  func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {     let rootVC = UIViewController(nibName: nil, bundle: nil)     let tableVC = UITableViewController(style: .Plain)     let navVC = UINavigationController(rootViewController: rootVC)     navVC.navigationBarHidden = true      navVC.view.backgroundColor = UIColor.redColor()     rootVC.view.backgroundColor = UIColor.greenColor()      rootVC.view.addSubview(tableVC.view)      //YOU NEED TO CONFIGURE THE VIEWS FRAME     //If you comment this out you will see the green view under the status bar     tableVC.view.frame = rootVC.view.bounds     tableVC.view.autoresizingMask = .FlexibleWidth | .FlexibleHeight     tableVC.view.setTranslatesAutoresizingMaskIntoConstraints(true)      rootVC.addChildViewController(tableVC)     tableVC.didMoveToParentViewController(rootVC)      window = UIWindow(frame: UIScreen.mainScreen().bounds)     window?.rootViewController = navVC     window?.makeKeyAndVisible()      return true } 

Note I wrote a post recently describing the ways you can configure a view to fill its superview

like image 74
Daniel Galasko Avatar answered Sep 30 '22 18:09

Daniel Galasko