ios layout uiviewcontroller xamarin

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 en true en UIViewController , UITableViewController y ambos
  • Jugar con EdgesForExtendedLayout y ExtendedLayoutIncludesOpaqueBars para UIViewController y UITableViewController
  • Jugado con UIViewController AutomaticallyAdjustsScrollViewInsets en el UIViewController
  • Jugado con PreservesSuperviewLayoutMargins en UITableView
  • Intentó invalidar PrefersStatusBarHidden y devolver true en UIViewController y UITableViewController

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 ? ¿El UITableViewController ?
  • ¿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 }

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


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 ^):

  1. En lugar de usar un UITableViewController , agregue un UIViewController y luego agregue un UITableView como elemento secundario del UIViewController . Si haces esto, entonces no hay un problema de 20 puntos. Esto resalta que el problema es con la clase UITableViewController .

    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í.

  2. 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.

  3. 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.