Pourquoi l'écart de 20px au sommet de mon UIViewController?

Le Problème

considérer la hiérarchie suivante du contrôleur:

  • UINavigationController
    • UIViewController
      • UITableViewController

la présence du UIViewController affecte la disposition. Sans lui ,le UITableViewController reprend la totalité des limites du UINavigationController :

enter image description here

cependant, si j'ajoute une vanille UIViewController entre le UINavigationController et UITableViewController , un écart de 20px apparaît entre le haut du UIViewController et le haut du UITableViewController :

enter image description here

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é app:

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

ci-dessus encore montre une 20px écart entre le haut de la UIView et le haut de la UITableView .

ma compréhension du problème

je comprends que quelque chose est, à tort, l'allocation d'espace disque pour une barre d'état. En utilisant Reveal je peux voir que le Frame du UITableViewController a une valeur de Y de 20 .

choses que j'ai Essayé

sans succès

  • Set WantsFullScreenLayout à true sur le UIViewController , UITableViewController , et les deux
  • jouant avec EdgesForExtendedLayout et ExtendedLayoutIncludesOpaqueBars pour les deux UIViewController et UITableViewController
  • joué avec AutomaticallyAdjustsScrollViewInsets sur le UIViewController
  • joué avec PreservesSuperviewLayoutMargins dans le UITableView
  • PrefersStatusBarHidden et le renvoi true dans les deux UIViewController et UITableViewController

Succès

Substitution ViewDidLayoutSubviews dans mon UITableViewController ainsi:

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

choses que je veux savoir

  • y a-t-il une(meilleure) façon d'atteindre mon objectif?
  • qu'est-ce qui est en fait responsable de l'ajout de l'écart de 20px? Le UIViewController ? Le UITableViewController ?
  • quelles sont les meilleures pratiques pour garantir que les contrôleurs my view restent utilisables dans des contextes différents? Le fait de remplacer ViewDidLayoutSubviews par "my view controller" jumelle probablement les attentes quant à l'endroit où il sera affiché dans l'arbre visuel. S'il devait être hébergé plus haut dans la pile du contrôleur, les choses ne seraient pas bien. Est-il un moyen pour éviter ce couplage et donc d'augmenter la réutilisabilité?
43
demandé sur Daniel Galasko 2015-05-29 11:59:58

5 réponses

le comportement que vous voyez n'est pas un bug du tout, mais simplement un effet secondaire de votre mauvaise utilisation d'ajouter des vues dans une hiérarchie.

lorsque vous ajoutez tableView dans une vue, vous devez dire à UIKit comment vous voulez que tableView soit de taille par rapport à son parent. Vous avez deux options: Auto-Layout ou des masques Auto-dimensionnement. Sans décrire comment vous voulez que votre vue à layout UIKit simplement pops sur la hiérarchie et la valeur par défaut la mise en œuvre placera votre vue sous le guide de mise en page du haut (qui se trouve juste ê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 exclusif de UITableViewController mais aussi de UICollectionViewController . Je crois que leur implémentation de chargement de vue par défaut insère la vue sous la barre d'état. Si nous transformons notre contrôleur d'enfants en une simple sous-classe UIViewController , aucun de ces comportements ne se manifeste. Ne voyez pas cela comme un bug cependant, si vous déclarez explicitement comment vous voulez que leurs vues respectives soient présentées, vous n'aurez pas ce problème. Naturellement, c'est la fonction principale d'un conteneur de contrôleur.

Voici ce que votre appDelegate devrait ressembler à:

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 j'ai écrit un post récemment décrivant les façons dont vous peut configurer une vue pour remplir sa superview

36
répondu Daniel Galasko 2017-05-23 12:10:38

votre problème semble se rapporter à une sorte de questions fondamentales avec UITableViewController étant ajouté directement en tant que contrôleur enfant d'un autre.

en regardant autour de ce n'est pas une nouvelle question: iOS 7: UITableView montre sous la barre de statut

par rapport à cet article, j'ai pris votre code et essayé les options suivantes (Une fois que j'ai réalisé qu'il était en C# 8^):

  1. au lieu de en utilisant un UITableViewController , ajouter un UIViewController et ensuite ajouter un UITableView comme un enfant du UIViewController . Si vous faites cela alors il n'y a pas de question 20pt. Cela met en évidence que l' le problème est avec la classe UITableViewController .

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

    j'ai essayé d'ajouter des contraintes dans votre code original pour forcer le Le cadre de controller de viewcontroller utilisable au sommet, mais n'a pas pu obtenir ce travail. Encore une fois, cela pourrait être en bas de la table contrôleur supérieur les choses lui-même.

  2. si vous construisez votre démo en utilisant des storyboards, tout fonctionne. Ce Que Je croire est dû au fait que L'IB lui-même utilise une vue de intégrer le nouveau contrôleur de vue. Donc, si vous utilisez storyboard, la façon apple le fait, c'est pour ajouter un point de vue qui vous pouvez définir le cadre de l'aide les contraintes et il intègre ensuite le UITableViewController à l'intérieur cette vue via une séquence embed.

    donc selon 1), l'utilisation d'une vue au milieu semble résoudre le problème et encore il semble que d'avoir le contrôle sur les vues moyennes frame est la clé.

    je remarque dans votre seul contournement viable, que changer le cadre a été réponse. Cependant, après iOS7, changer le cadre ne semble pas être recommandé en raison des problèmes qu'il peut avoir heurter avec les contraintes qui veulent aussi manipuler l'image.

  3. Essayer d'autres options comme edgesForExtendedLayout tout semblait échouer. Ceux-ci semblent être des conseils pour les contrôleurs de vue de conteneur et Uitable viewcontroller les ignore.

IMHO je pense que l'option 1) semble l'approche la plus sûre car vous avez le contrôle total sur la disposition et ne combattez pas le système avec des dérogations de cadre qui peuvent vous causer des problèmes plus tard. Option 2) ne fonctionne vraiment si vous utilisez storyboards. Vous pouvez essayer de faire la même chose manuellement vous-même, mais qui sait ce qui se passe dans une séquence embed.

MODIFIER

il semblerait qu'il y avait une étape manquante comme souligné par Arkadiusz Holko dans sa réponse et le réglage du cadre explicitement pour la vue de la table ne corriger la question.

8
répondu Rory McKinnel 2017-05-23 12:34:34

vous avez manqué une étape lors de l'ajout d'un contrôleur de vue enfant – configurer le cadre de sa vue.

voir 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

5
répondu Arek Holko 2015-06-02 18:25:19

20 Pixels est pris par la barre d'état. Si vous utilisez un fichier xib ou un storyboard avec mise en page automatique, vous pouvez définir la contrainte supérieure à TOP layout guide de sorte que la différence de 20 pixels soit traitée

4
répondu vineeth s thayyil 2015-06-01 07:07:17

y a-t-il une(meilleure) façon d'atteindre mon objectif?

il suffit de changer 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 Contrôleur D'UIViewController? Le Contrôleurviewcontable?

vous pouvez enregistrer UIViewController.view.frame après avoir ajouté un UIViewController . Vous pouvez voir UITableViewController.view.frame ont toujours un écart de 20px. Pourquoi? Je pense Qu'Apple vient d'initialiser UITableViewController.view avec taille d'écran avec un rembourrage supérieur de 20px.

quelles sont les meilleures pratiques pour garantir que les contrôleurs my view restent utilisables dans des contextes différents? ...

si vous voulez ajouter un UIViewController.view à un autre UIViewController.view .La meilleure façon est d'utiliser le story board et d'utiliser Container View .

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

2
répondu PowHu 2015-06-02 09:59:06