Pourquoi l’écart de 20px en haut de mon UIViewController?

Le problème

Considérez la hiérarchie de contrôleur suivante:

  • UINavigationController
    • UIViewController
      • UITableViewController

La présence de UIViewController affecte la disposition. Sans cela, UITableViewController prend en charge l’intégralité des limites de UINavigationController :

entrer la description de l'image ici

Cependant, si j’ajoute un UIViewController vanille entre UINavigationController et UITableViewController , un écart de 20px apparaît entre le haut de l’ UIViewController et le haut de UITableViewController :

entrer la description de l'image ici

Même si je réduis mon code à la chose la plus simple possible, j’observe toujours ce comportement. Considérez ce code de délégué d’application:

 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; } 

Le ci-dessus montre encore un écart de 20px entre le haut de l’ UIView et le haut de l’ UITableView .

Ma compréhension du problème

Je comprends que quelque chose atsortingbue à tort de l’espace à une barre d’état. En utilisant Reveal, je peux voir que le Frame de UITableViewController a une valeur Y de 20 .

Les choses que j’ai essayées

Infructueux

  • Définissez WantsFullScreenLayout sur true sur UIViewController , UITableViewController et les deux
  • Lecture avec EdgesForExtendedLayout et ExtendedLayoutIncludesOpaqueBars à la fois pour UIViewController et UITableViewController
  • Joué avec AutomaticallyAdjustsScrollViewInsets sur UIViewController
  • Joué avec PreservesSuperviewLayoutMargins dans UITableView
  • Essayé de PrefersStatusBarHidden et de retourner true dans UIViewController et UITableViewController

Réussi

ViewDidLayoutSubviews de ViewDidLayoutSubviews dans mon UITableViewController :

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

Les choses que je veux savoir

  • Y a-t-il un moyen propre d’atteindre mon objective?
  • Qu’est-ce qui est réellement responsable de l’ajout de l’écart de 20px? Le UIViewController ? Le UITableViewController ?
  • Quelles sont les meilleures pratiques pour garantir que mes contrôleurs de vue restnt utilisables dans différents contextes? En ViewDidLayoutSubviews mon contrôleur de vue aux attentes quant à l’endroit où il sera affiché dans l’arborescence visuelle. S’il devait être hébergé plus haut dans la stack de contrôleurs, les choses ne sembleraient pas correctes. Y a-t-il un moyen d’éviter ce couplage et d’augmenter ainsi la réutilisabilité?

Le comportement que vous observez n’est pas du tout un bogue, mais simplement un effet secondaire de votre mauvaise utilisation de l’ajout de vues dans une hiérarchie.

Lorsque vous ajoutez la tableView dans une vue, vous devez indiquer à UIKit comment vous souhaitez que cette tableView soit dimensionnée par rapport à son parent. Vous avez deux options: Mise en page automatique ou Autoresizing Masques. Sans décrire la manière dont vous voulez voir votre présentation, UIKit simplement dans la hiérarchie et l’implémentation par défaut affichera votre vue sous le guide de mise en page supérieur (qui se trouve être la hauteur de la barre d’état). Quelque chose d’aussi simple que cela ferait l’affaire:

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

Ce comportement n’est pas réellement exclusif à UITableViewController mais également à UICollectionViewController . Je crois que leur implémentation par défaut de viewLoading insère la vue sous la barre d’état. Si nous faisons de notre childController une simple sous-classe UIViewController aucun de ces comportements n’est présenté. Ne voyez pas cela comme un bogue si vous déclarez explicitement comment vous souhaitez que leurs vues respectives soient disposées, vous n’aurez pas ce problème. C’est naturellement la fonction principale d’un contrôleur de conteneur.

Voici à quoi devrait ressembler votre appDelegate:

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; } 

Rapide

 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 J’ai récemment écrit un article décrivant comment configurer une vue pour remplir sa vue d’ensemble

Votre problème semble être UITableViewController à certains problèmes fondamentaux, UITableViewController étant directement ajouté en tant que contrôleur enfant d’un autre.

Regarder autour de vous n’est pas un nouveau problème: iOS 7: UITableView s’affiche sous la barre d’état

Par rapport à cet article, j’ai pris votre code et essayé les options suivantes (une fois que j’ai réalisé que c’était en C # 8 ^)):

  1. Au lieu d’utiliser un UITableViewController , ajoutez un UIViewController , puis ajoutez un UITableView tant qu’enfant de UIViewController . Si vous faites cela, il n’y a pas de problème 20pt. Cela souligne que le problème est avec la classe UITableViewController .

    Cette approche est une solution relativement simple et ne comporte que quelques étapes supplémentaires sur ce que vous avez déjà.

    J’ai essayé d’append des contraintes dans votre code d’origine pour forcer le cadre UITableViewController vers le haut, mais cela ne pouvait pas fonctionner. Encore une fois, cela pourrait être dû au contrôleur de la table qui surpasse les choses lui-même.

  2. Si vous construisez votre démo en utilisant des storyboards, tout fonctionne. Je pense que cela tient au fait que IB lui-même utilise une vue de conteneur pour intégrer le nouveau contrôleur de vue. Donc, si vous utilisez un storyboard, la façon dont Apple le fait est d’append une vue sur laquelle vous pouvez définir le cadre d’utilisation des contraintes, puis d’intégrer UITableViewController dans cette vue via un Segue intégré.

    Par conséquent, comme pour 1), utiliser une vue au milieu semble résoudre le problème et, à nouveau, il semble que le contrôle du frame vues du milieu soit la clé.

    Je remarque dans votre seule solution de contournement viable que changer de cadre était la solution. Cependant, après iOS7, changer de cadre ne semble pas être recommandé en raison des problèmes qu’il peut avoir avec les contraintes qui veulent également manipuler le cadre.

  3. Essayer d’autres options comme edgesForExtendedLayout semblait échouer. Celles-ci semblent être des indices pour les contrôleurs de vue de conteneur et UITableViewController les ignore.

IMHO Je pense que l’option 1) semble l’approche la plus sûre car vous avez un contrôle total sur la mise en page et ne combattez pas le système avec des dépassements de cadre qui peuvent vous causer des problèmes plus tard. Option 2) ne fonctionne vraiment que si vous utilisez des storyboards. Vous pouvez essayer de faire la même chose manuellement, mais qui sait ce qui se passe dans une Segue intégrée.

MODIFIER

Il semblerait qu’il y ait eu une étape manquante, comme le souligne Arkadiusz Holko dans sa réponse, et le fait de définir explicitement le cadre pour la vue de tableau corrige le problème.

Vous avez manqué une étape lors de l’ajout d’un contrôleur de vue enfant – définissant le cadre de sa vue.

Voir la deuxième étape:

 - (void) displayContentController: (UIViewController*) content; { [self addChildViewController:content]; // 1 content.view.frame = [self frameForContentController]; // 2 [self.view addSubview:self.currentClientView]; [content didMoveToParentViewController:self]; // 3 } 

Source: https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/CreatingCustomContainerViewControllers/CreatingCustomContainerViewControllers.html

20 pixels sont pris par la barre d’état. Si vous utilisez un fichier xib ou un storyboard avec une mise en page automatique, vous pouvez définir la contrainte supérieure sur le guide de disposition supérieur afin que la différence de 20 pixels soit gérée

Existe-t-il un moyen propre d’atteindre mon objective?

Changez simplement le cadre.

 tableView.View.Frame = intermediateView.View.Bounds; tableView.View.AutoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 

Qu’est-ce qui est réellement responsable de l’ajout de l’écart de 20px? Le UIViewController? Le UITableViewController?

Vous pouvez vous connecter à UIViewController.view.frame après avoir UIViewController un UIViewController . Vous pouvez voir que UITableViewController.view.frame toujours un écart de 20px. Pourquoi? Je pense qu’Apple initialise UITableViewController.view avec une taille d’écran de 20px.

Quelles sont les meilleures pratiques pour garantir que mes contrôleurs de vue restnt utilisables dans différents contextes?

Si vous souhaitez append une vue UIViewController.view à une autre UIViewController.view meilleure méthode consiste à utiliser le UIViewController.view et à utiliser la Container View .

Si vous ne voulez pas ou ne pouvez pas utiliser le story-board. Je suggère juste la sous-classe UIView . addChildViewController parfois des problèmes avec le cercle de vie et la disposition.