ios - ¿Por qué la brecha de 20 píxeles en la parte superior de mi UIViewController?
layout xamarin (5)
20 píxeles son tomados por la barra de estado. Si está utilizando un archivo xib o un guión gráfico con diseño automático, puede establecer la restricción superior en la guía de diseño superior para que se maneje la diferencia de 20 píxeles.
El problema
Considere la siguiente jerarquía de controlador:
UINavigationController
-
UIViewController
-
UITableViewController
-
-
La presencia de UIViewController
está afectando el diseño. Sin él, UITableViewController
ocupa todos los límites del UINavigationController
:
Sin embargo, si agrego un UIViewController
vainilla entre UINavigationController
y UITableViewController
, aparece un espacio de 20 UIViewController
entre la parte superior del UIViewController
y la parte superior del UITableViewController
:
Incluso si reduzco mi código a lo más simple posible, aún observo este comportamiento. Considere este código de delegado de aplicación:
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;
}
Lo anterior aún muestra un espacio de 20 UIView
entre la parte superior de UIView
y la parte superior de UITableView
.
Mi comprensión del problema
Entiendo que algo está asignando erróneamente espacio para una barra de estado. Usando Reveal, puedo ver que el Frame
del UITableViewController
tiene un valor Y
de 20
.
Cosas que he probado
Fracasado
- Establezca
WantsFullScreenLayout
entrue
enUIViewController
,UITableViewController
y ambos - Jugar con
EdgesForExtendedLayout
yExtendedLayoutIncludesOpaqueBars
paraUIViewController
yUITableViewController
- Jugado con
UIViewController
AutomaticallyAdjustsScrollViewInsets
en elUIViewController
- Jugado con
PreservesSuperviewLayoutMargins
enUITableView
- Intentó invalidar
PrefersStatusBarHidden
y devolvertrue
enUIViewController
yUITableViewController
Exitoso
Sobreescribiendo ViewDidLayoutSubviews
en mi UITableViewController
así:
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
this.View.Frame = this.View.Superview.Bounds;
}
Cosas que quiero saber
- ¿hay una forma limpia (er) de lograr mi objetivo?
- ¿Qué es realmente responsable de agregar la brecha de 20px? ¿
UIViewController
? ¿ElUITableViewController
? - ¿Cuáles son las mejores prácticas para garantizar que mis controladores de vista sigan siendo utilizables en diferentes contextos? Presumiblemente, la anulación de
ViewDidLayoutSubviews
acopla mi controlador de vista a las expectativas de dónde se mostrará en el árbol visual. Si fuera alojado más arriba en la pila del controlador, las cosas no se verían bien. ¿Hay alguna manera de evitar este acoplamiento y así aumentar la reutilización?
El comportamiento que está viendo no es un error en absoluto, sino simplemente un efecto secundario de su uso indebido de agregar vistas a una jerarquía.
Cuando agrega tableView a una vista, necesita decirle a UIKit cómo desea que tableView tenga el tamaño relativo a su elemento principal. Tiene dos opciones: Auto-Layout o Autoresizing Masks. Sin describir cómo desea que su vista se vea, el diseño de UIKit
simplemente lo UIKit
en la jerarquía y la implementación predeterminada ubicará su vista debajo de la guía de diseño superior (que simplemente es el alto de la barra de estado). Algo tan simple como esto haría el truco:
tableVC.View.Frame = rootVC.View.Bounds
tableVC.View.Autoresizingmask = UIViewAutoresizing.FlexibleWidth
tableVC.View.TranslatesAutoresizingMaskIntoConstraints = true// always nice to explicitly use auto layout
Este comportamiento no es exclusivo de UITableViewController
sino también de UICollectionViewController
. Creo que su implementación viewLoading predeterminada inserta la vista debajo de la barra de estado. Si hacemos que nuestro childController sea una subclase simple UIViewController
no se exhibe ninguno de estos comportamientos. Sin embargo, no veo esto como un error, si declaras explícitamente cómo quieres que se expongan sus vistas respectivas, no tendrás este problema. Naturalmente, esta es la función principal de un controlador de contenedor.
Aquí es cómo debería ser su aplicaciónDelegate:
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;
}
Rápido
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
}
Nota : escribí una publicación que describía recientemente las formas en que puede configurar una vista para llenar su supervista
Perdió un paso al agregar un controlador de vista secundaria, configurando el marco de su vista.
Vea el segundo paso:
- (void) displayContentController: (UIViewController*) content;
{
[self addChildViewController:content]; // 1
content.view.frame = [self frameForContentController]; // 2
[self.view addSubview:self.currentClientView];
[content didMoveToParentViewController:self]; // 3
}
Su problema parece estar relacionado con algún tipo de problemas fundamentales con UITableViewController
que se agrega directamente como un controlador secundario de otro.
Mirar a su alrededor no es un problema nuevo: iOS 7: UITableView se muestra debajo de la barra de estado
En relación con ese artículo, tomé su código y probé las siguientes opciones (una vez que me di cuenta de que estaba en C # 8 ^):
En lugar de usar un
UITableViewController
, agregue unUIViewController
y luego agregue unUITableView
como elemento secundario delUIViewController
. Si haces esto, entonces no hay un problema de 20 puntos. Esto resalta que el problema es con la claseUITableViewController
.Este enfoque es una solución relativamente limpia y solo un par de pasos adicionales en lo que ya tiene.
Intenté agregar restricciones en tu código original para forzar el marco UITableViewController a la parte superior, pero no pude hacer que esto funcionara. De nuevo, esto podría deberse a que el controlador de la tabla anula las cosas en sí.
Si construyes tu demo usando storyboards, todo funciona. Esto creo que se debe al hecho de que IB usa una vista de contenedor para insertar el nuevo controlador de vista. Por lo tanto, si usa el guión gráfico, la forma en que Apple lo hace es agregar una vista que puede establecer el marco de uso de restricciones y luego incorpora el
UITableViewController
dentro de esa vista a través de un Segue incrustado.Por lo tanto, como en 1), usar una vista en el medio parece resolver el problema y nuevamente parece que tener control sobre el
frame
vistas centrales es clave.Observo en su única solución viable, que cambiar el marco fue la respuesta. Sin embargo, después de iOS7, no parece recomendable cambiar el marco debido a los problemas que puede tener al enfrentarse con las restricciones que también quieren manipular el marco.
- Probar otras opciones como
edgesForExtendedLayout
parecía fallar. Estos parecen ser pistas para controladores de vista de contenedor y UITableViewController los está ignorando.
En mi humilde opinión, creo que la opción 1) parece ser el enfoque más seguro, ya que tienes un control total sobre el diseño y no estás peleando con las anulaciones de fotogramas que pueden causarte problemas más adelante. Opción 2) solo funciona realmente si usa storyboards. Podría intentar hacer lo mismo manualmente, pero quién sabe qué sucede en un Segue incrustado.
EDITAR
Parecería que hubo un paso faltante, como lo destacó Arkadiusz Holko en su respuesta y establecer explícitamente el marco para que la vista de tabla solucione el problema.
¿Hay una manera limpia (er) de lograr mi objetivo?
Solo cambia el marco.
tableView.View.Frame = intermediateView.View.Bounds;
tableView.View.AutoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
¿Qué es realmente responsable de agregar la brecha de 20px? ¿UIViewController? ¿El UITableViewController?
Puede registrar UIViewController.view.frame
después de haber UIViewController
un UIViewController
. Puede ver que UITableViewController.view.frame
siempre tiene un espacio de 20 UITableViewController.view.frame
. ¿Por qué? Creo que Apple acaba de inicializar UITableViewController.view
con el tamaño de la pantalla con un relleno superior de 20 píxeles.
¿Cuáles son las mejores prácticas para garantizar que mis controladores de vista sigan siendo utilizables en diferentes contextos? ...
Si desea agregar un UIViewController.view
a otro UIViewController.view
. La mejor manera es usar story board y usar Container View
.
Si no quieres o no puedes usar story board. Sugiero solo la subclase UIView
. addChildViewController
veces tiene problemas molestos con el círculo de vida y el diseño.