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
:
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
:
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
.
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
.
WantsFullScreenLayout
to true
on the UIViewController
, UITableViewController
, and bothEdgesForExtendedLayout
and ExtendedLayoutIncludesOpaqueBars
for both the UIViewController
and UITableViewController
AutomaticallyAdjustsScrollViewInsets
on the UIViewController
PreservesSuperviewLayoutMargins
in the UITableView
PrefersStatusBarHidden
and returning true
in both the UIViewController
and UITableViewController
Overriding ViewDidLayoutSubviews
in my UITableViewController
thusly:
public override void ViewDidLayoutSubviews() { base.ViewDidLayoutSubviews(); this.View.Frame = this.View.Superview.Bounds; }
UIViewController
? The UITableViewController
?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?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).
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.
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.
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.
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:
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; }
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With