iOS 7: UITableView se muestra debajo de la barra de estado
ios7 uistatusbar (26)
La primera pantalla de mi aplicación es un UITableViewController
sin barra de navegación, lo que significa que el contenido fluye bajo la barra de estado, por lo que hay muchas colisiones de texto. He ajustado ambas propiedades para Under top bars
y Adjust scroll view insets
que realmente impiden que se desplace hacia abajo, pero al costo de mantener la parte superior de la vista de tabla debajo. He intentado configurar el marco de la UITableView
para que se UITableView
en 20 píxeles, pero no parece que tenga efecto y, dado que actualmente necesito que la aplicación sea compatible con iOS 6, no puedo saltar a iOS 7 Storyboards para forzar la reproducción automática. Utilice la guía de altura superior. ¿Alguien ha encontrado una solución que funcione para ambas versiones?
Cosas que he intentado: configuración de edgesForExtendedLayout
, cambio de la configuración dentro del guión gráfico para Under top bars
y Adjust scroll view
, forzando el marco a una nueva área.
Una imagen vale mas que mil palabras:
Agregando a la respuesta superior:
después de que el segundo método no pareció funcionar inicialmente, hice algunos retoques adicionales y encontré la solución.
TLDR; la segunda solución de la respuesta principal casi funciona, pero para algunas versiones de xCode ctrl + arrastrando a la "Guía de diseño superior" y seleccionar Espaciado vertical no hace nada. Sin embargo, al primero ajustar el tamaño de la Vista de tabla y luego seleccionar "Espacio superior a la Guía de diseño superior" funciona
Arrastre un ViewController en blanco al guión gráfico.
Arrastre un objeto UITableView a la Vista. (No UITableViewController). Colóquelo en el centro usando las guías de diseño azules.
- Arrastre un UITableViewCell en el TableView. Esta será su celda de reutilización de prototipo, así que no olvide configurar su Identificador de Reutilización en la pestaña Atributos o se bloqueará.
Cree su subclase personalizada de UIViewController y agregue los
<UITableViewDataSource, UITableViewDelegate>
. No olvide configurar el ViewController de su guión gráfico para esta clase en el inspector de identidad.Cree una salida para su TableView en su archivo de implementación, y denomínelo "tableView"
- Haga clic derecho en TableView y arrastre tanto el DataSource como el delegado a su ViewController.
Ahora para la parte de no recortar en la barra de estado.
- Tome el borde superior de su Vista de tabla y muévalo hacia abajo a una de las guías de diseño automático azul discontinuas que están cerca de la parte superior
- Ahora, puede controlar el arrastre desde la vista de tabla hasta la parte superior y seleccionar la guía de Top Space to Top Layout
- Te dará un error sobre el diseño ambiguo de TableView, solo agrega Restricciones faltantes y listo.
Ahora puede configurar su vista de tabla como de costumbre, ¡y no recortará la barra de estado!
Aquí hay un Swift 2.3. (Xcode 8.0) solución. He creado una subclase de UITableView.
class MYCustomTableView: UITableView
{
override func drawRect(rect: CGRect) {
super.drawRect(rect)
contentInset = UIEdgeInsetsZero
}
}
El contenido del recuadro siempre debe ser cero (de forma predeterminada). Lo estoy poniendo a cero manualmente. También puede agregar un método de verificación que haga la verificación y, si es algo distinto a lo que desea, solo obtenga el correcto. El cambio se reflejará solo cuando se dibuje la vista de tabla (lo que no sucede a menudo).
No olvide actualizar el TableView en su IB (en el TableViewController o simplemente TableView dentro de su ViewController).
Así es como se escribe en "Swift" Un ajuste a la respuesta de @ lipka:
tableView.contentInset = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
Creo que el enfoque para utilizar UITableViewController puede ser un poco diferente de lo que has hecho antes. Me ha funcionado, pero puede que no seas un fanático de ello. Lo que he hecho es tener un controlador de vista con una vista de contenedor que apunta a mi UItableViewController. De esta manera puedo usar el TopLayoutGuide proporcionado en mi guión gráfico. Simplemente agregue la restricción a la vista de contenedor y debe estar atento a iOS7 y iOS6.
Esto lo arreglará para un UITableViewController (sin ningún número mágico). Lo único que no pude solucionar es si está realizando una llamada telefónica, en cuyo caso la parte superior de la tableView se presiona demasiado. Si alguien sabe cómo resolverlo, háganoslo saber.
class MyTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureTableViewTop()
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({ (context) -> Void in
}, completion: { (context) -> Void in
self.configureTableViewTop()
})
}
func configureTableViewTop() {
tableView.contentInset.top = UIApplication.sharedApplication().statusBarFrame.height
}
}
Estoy utilizando un UISplitViewController con un controlador de navegación y un controlador de vista de tabla. Esto funcionó para mí en la vista maestra después de probar muchas soluciones aquí:
float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {
// Move the view down 20 pixels
CGRect bounds = self.view.bounds;
bounds.origin.y -= 20.0;
[self.navigationController.view setBounds:bounds];
// Create a solid color background for the status bar
CGRect statusFrame = CGRectMake(0.0, -20.0, bounds.size.width, 20);
UIView* statusBar = [[UIView alloc] initWithFrame:statusFrame];
statusBar.backgroundColor = [UIColor whiteColor];
[statusBar setAlpha:1.0f];
[statusBar setOpaque:YES];
[self.navigationController.view addSubview:statusBar];
}
Es similar a la solución de Hot Licks, pero aplica la subvista al navigationController.
He hecho esto para Retina / Non-Retina display como
BOOL isRetina = FALSE;
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
isRetina = TRUE;
} else {
isRetina = FALSE;
}
}
if (isRetina) {
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;
}
La respuesta de chappjc funciona muy bien cuando se trabaja con XIB.
Encontré que la solución más limpia al crear TableViewControllers mediante programación es envolver la instancia de UITableViewController en otro UIViewController y establecer restricciones en consecuencia.
Aquí está:
UIViewController *containerLeftViewController = [[UIViewController alloc] init];
UITableViewController *tableViewController = [[UITableViewController alloc] init];
containerLeftViewController.view.backgroundColor = [UIColor redColor];
hostsAndMoreTableViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[containerLeftViewController.view addSubview:tableViewController.view];
[containerLeftViewController addChildViewController:tableViewController];
[tableViewController didMoveToParentViewController:containerLeftViewController];
NSDictionary * viewsDict = @{ @"tableView": tableViewController.view ,
@"topGuide": containerLeftViewController.topLayoutGuide,
@"bottomGuide": containerLeftViewController.bottomLayoutGuide,
};
[containerLeftViewController.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tableView]|"
options:0
metrics:nil
views:viewsDict]];
[containerLeftViewController.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide][tableView][bottomGuide]"
options:0
metrics:nil
views:viewsDict]];
Saludos, Ben
La siguiente solución funciona lo suficientemente bien en el código sin usar constantes mágicas, y da cuenta de que el usuario cambia la clase de tamaño, por ejemplo, mediante rotaciones o aplicaciones en paralelo en los iPads:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
// Fix up content offset to ensure the tableview isn''t underlapping the status bar.
self.tableView.contentInset = UIEdgeInsetsMake(self.topLayoutGuide.length, 0.0, 0.0, 0.0);
}
La solución de Abrahamchez https://developer.apple.com/library/ios/qa/qa1797/_index.html funcionó para mí de la siguiente manera. Tenía un solo controlador de vista de UIT como mi vista inicial. Probé el código de compensación e incrusté en un navegador, pero ninguno resolvió la transparencia de la barra de estado.
Agregue un controlador de vista y conviértalo en la vista inicial. Esto debería mostrarle las guías de diseño superior e inferior críticas.
Arrastre la Tableview antigua a la Vista en el nuevo controlador.
Haga todo lo necesario para adaptar la tabla al nuevo controlador:
Cambie su antiguo view controller.h para heredar / subclase de UIViewController en lugar de UITableViewController.
Agregue UITableViewDataSource y UITableViewDelegate al controlador de visualización .h.
Vuelva a conectar todo lo necesario en el guión gráfico, como una barra de búsqueda.
Lo importante es configurar las restricciones, como en las preguntas y respuestas de Apple. No me molesté en insertar una barra de herramientas. No es seguro la secuencia exacta. Pero un icono rojo apareció en las Guías de diseño, tal vez cuando construí. Hice clic en él y dejé que Xcode instale / limpie las restricciones.
Luego hice clic en todas partes hasta que encontré la restricción del espacio vertical y cambié su valor superior de -20 a 0 y funcionó perfectamente.
Lo tengo funcionando ajustando el tamaño a la forma libre
No sé qué tan kosher es, pero descubrí que este esquema mueve hacia abajo la vista del Controlador de Vista y proporciona la barra de estado con un fondo sólido:
- (void)viewDidLoad {
[super viewDidLoad];
// Shove everything down if iOS 7 or later
float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {
// Move the view down 20 pixels
CGRect bounds = self.view.bounds;
bounds.origin.y -= 20.0;
[self.view setBounds:bounds];
// Create a solid color background for the status bar
CGRect statusFrame = CGRectMake(0.0, -20.0, bounds.size.width, 20);
UIView* statusBar = [[UIView alloc] initWithFrame:statusFrame];
statusBar.backgroundColor = [UIColor redColor];
[self.view addSubview:statusBar];
}
Por supuesto, reemplaza el color redColor
con el color que quieras para el fondo.
Debe hacer por separado uno de los swizzles para establecer el color de los caracteres / símbolos en la barra de estado. Uso la View controller-based status bar appearance = NO
y el Status bar style = Opaque black style
en la lista, para hacer esto globalmente.
Parece que funciona, y me interesaría saber de cualquier error o problema con él.
Para aquellos como yo que preferirían no incrustar su UITableViewController
en un UIViewController
intente esto:
Una subclase UITableViewController
personalizada puede agregar una vista de máscara a la vista de tabla de la vista de tabla. Agregue la máscara en viewDidAppear y elimine la máscara en viewWillDisappear.
private var statusBarMaskView: UIView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let superview = self.tableView.superview {
self.statusBarMaskView = UIView(frame: CGRect.zero)
superview.insertSubview(self.statusBarMaskView, aboveSubview: self.tableView)
self.statusBarMaskView.backgroundColor = self.tableView.backgroundColor
// using a nice constraint layout library to set the frame
self.statusBarMaskView.pinTop(to: superview)
self.statusBarMaskView.pinLeft(to: superview)
self.statusBarMaskView.pinRight(to: superview)
self.statusBarMaskView.addHeightConstraint(with: 22.0)
////
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.statusBarMaskView.removeFromSuperview()
self.statusBarMaskView = nil
}
Para cualquier persona interesada en replicar esto, simplemente siga estos pasos:
- Crea un nuevo proyecto iOS
- Abra el guión gráfico principal y elimine el
UIViewController
predeterminado / inicial - Arrastre un nuevo
UITableViewController
desde la biblioteca de objetos - Establézcalo como el controlador de vista inicial
- Alimentar la tabla algunos datos de prueba
Si sigue los pasos anteriores, cuando ejecute la aplicación, verá que nada, incluyendo el ajuste de las casillas de verificación de Xcode a "Extender bordes debajo de {Top, Bottom, Opaque} Bars", impide que la primera fila aparezca debajo de la barra de estado. ni puedes abordar esto programáticamente.
Por ejemplo, en el escenario anterior, lo siguiente no tendrá ningún efecto:
// These do not work
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;
Este problema puede ser muy frustrante, y creo que es un error en el extremo de Apple, especialmente porque aparece en su propio UITableViewController
precableado desde la biblioteca de objetos.
No estoy de acuerdo con todos los que intentan resolver esto utilizando cualquier forma de "Números mágicos", por ejemplo, "use un delta de 20px". Este tipo de programación estrechamente acoplada definitivamente no es lo que Apple quiere que hagamos aquí.
He descubierto dos soluciones a este problema:
Preservando la
UITableViewController
delUITableViewController
:
Si desea mantener elUITableViewController
en el guión gráfico, sin colocarlo manualmente en otra vista, puede incrustar elUITableViewController
en unUINavigationController
(Editor> Incrustar en> Controlador de navegación) y deseleccionar "Muestra la barra de navegación" en el inspector. Esto resuelve el problema sin necesidad de ajustes adicionales, y también conserva laUITableViewController
suUITableViewController
en el guión gráfico.Usando AutoLayout e incrustando el
UITableView
en otra vista (creo que así es como Apple quiere que hagamos esto) :
Cree unUIViewController
vacío y arrastre suUITableView
en él. Luego,UITableView
Ctrl y arrastre desde suUITableView
hacia la barra de estado. Cuando el mouse llegue a la parte inferior de la barra de estado, verá una burbuja de Autolayout que dice "Guía de diseño superior". Suelte el ratón y seleccione "Espaciado vertical". Eso le dirá al sistema de diseño que lo coloque justo debajo de la barra de estado.
He probado ambas formas en una aplicación vacía y ambas funcionan. Es posible que deba hacer algunos ajustes adicionales para que funcionen en su proyecto.
Seleccione UIViewController en su guión gráfico y desmarque la opción Extender bordes debajo de las barras superiores. Trabajó para mi. :)
Si está haciendo cosas programáticamente y está utilizando un UITableViewController
sin un UINavigationController
su mejor apuesta es hacer lo siguiente en viewDidLoad
:
Swift 3
self.tableView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
Antes Swift
self.tableView.contentInset = UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f);
El UITableViewController
seguirá UITableViewController
detrás de la barra de estado, pero no estará debajo cuando se desplace hacia la parte superior.
Si también necesitas soportar iOS 6, deberás moverlo hacia abajo de manera condicional. Es decir, en iOS 7 solo debes moverlo hacia abajo 20 puntos (ya sea a través de la manipulación de frame
o utilizando el diseño automático), y en iOS 6 lo dejas solo. No creo que puedas hacer esto en IB, así que tendrás que hacerlo en código.
EDITAR
En realidad, puede hacer esto en IB, utilizando los deltas de iOS6 / iOS7. Establezca su posición en iOS 7, luego para iOS 6 configure el delta Y en -20 puntos. Vea esta pregunta SO para más información.
Tenía una barra UISearch en la parte superior de mi UITableView y funcionó lo siguiente;
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
self.tableView.contentOffset = CGPointMake(0, -20);
Comparte y Disfruta...
Terminé usando una vista adicional con el fondo deseado, añadido después de TableView y colocado debajo de la barra de estado:
self.CoverView = [[UIView alloc]init];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
self.CoverView.frame = CGRectMake(0,0,self.view.bounds.size.width,20);
}
self.CoverView.backgroundColor = [UIColor whiteColor];
self.TableView = [[UITableView alloc]initWithFrame:CGRectMake(0,
self.CoverView.bounds.size.height,XXX, YYY)];
[self.view addSubview:self.TableView];
[self.view addSubview:self.CoverView];
No es muy bonito, pero es una solución bastante simple, si necesita trabajar con vistas sin xib, y tanto IOS6 como IOS7
Works para swift 3 - En viewDidLoad ();
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let insets = UIEdgeInsets(top: statusBarHeight, left: 0, bottom: 0, right: 0)
tableView.contentInset = insets
tableView.scrollIndicatorInsets = insets
Este código: 1. Obtiene la altura de la barra de estado 2. Da a la parte superior de la vista de tabla una inserción de contenido igual a la altura de la barra de estado. 3. Le da al indicador de desplazamiento la misma inserción, por lo que aparece debajo de la barra de estado.
vea todas las soluciones: mi proyecto es solo usar xib, así que la solución con storyboard no funcionó para mí. self.edgesForExtendedLayout = UIRectEdgeNone; Simplemente funciona para el controlador si la barra de navegación es visible. pero si su vista es simplemente tener barra de estado, eso no funcionará. Así que combino dos condiciones.
- (void) viewDidLayoutSubviews {
float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {
CGRect bounds = self.view.bounds;
if(self.navigationController == nil || self.navigationController.isNavigationBarHidden == YES){
bounds.origin.y -= 20.0;
[self.view setBounds:bounds];
}
else{
self.edgesForExtendedLayout = UIRectEdgeNone;
}
}
ayuda esto funciona.
Tenga en cuenta: Esto funcionó para mí para la siguiente configuración:
- No hay barra de navegación en la parte superior de la pantalla (la vista de tabla se encuentra con la barra de estado)
- La vista de tabla no es desplazable
Si los dos requisitos anteriores no se cumplen, su kilometraje puede variar.
Mensaje original
Creé mi vista programáticamente y esto terminó trabajando para mí:
- (void) viewDidLayoutSubviews {
// only works for iOS 7+
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
CGRect viewBounds = self.view.bounds;
CGFloat topBarOffset = self.topLayoutGuide.length;
// snaps the view under the status bar (iOS 6 style)
viewBounds.origin.y = topBarOffset * -1;
// shrink the bounds of your view to compensate for the offset
viewBounds.size.height = viewBounds.size.height + (topBarOffset * -1);
self.view.bounds = viewBounds;
}
}
Source (en la sección topLayoutGuide al final de la página 39).
- (void) viewDidLayoutSubviews {
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone; // iOS 7 specific
CGRect viewBounds = self.view.bounds;
CGFloat topBarOffset = self.topLayoutGuide.length;
viewBounds.origin.y = topBarOffset * -1;
self.view.bounds = viewBounds;
self.navigationController.navigationBar.translucent = NO;
}
}
override func viewDidLoad() {
// your code
if let nc = self.navigationController {
yourView.frame.origin.y = nc.navigationBar.frame.origin.y + nc.navigationBar.frame.height
}
}