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
:
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
:
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 leUIViewController
,UITableViewController
, et les deux - jouant avec
EdgesForExtendedLayout
etExtendedLayoutIncludesOpaqueBars
pour les deuxUIViewController
etUITableViewController
- joué avec
AutomaticallyAdjustsScrollViewInsets
sur leUIViewController
- joué avec
PreservesSuperviewLayoutMargins
dans leUITableView
-
PrefersStatusBarHidden
et le renvoitrue
dans les deuxUIViewController
etUITableViewController
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
? LeUITableViewController
? - 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é?
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
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^):
-
au lieu de en utilisant un
UITableViewController
, ajouter unUIViewController
et ensuite ajouter unUITableView
comme un enfant duUIViewController
. 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 classeUITableViewController
.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.
-
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.
- 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.
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
}
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
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.