UITableViewCell con autolayout margen izquierdo diferente en iPhone y iPad
Estoy usando un UITableView
agrupado con celdas estáticas para una pantalla/escena de opciones. Todo se hace en Xcode 6.1 / iOS 8.1.x / Storyboard usando diseño automático. Dentro de los grupos de tabla hay tipos mixtos de celdas y hay dos tipos que me causan problemas:
- Celdas con estilo personalizado y
- Celdas con estilo "Detalle derecho"
En la celda #1 puedo establecer una restricción para el margen izquierdo entre la etiqueta y el contenedor principal. En la celda # 2 no puedo establecer ninguna restricciones en Interface Builder por lo que sé. He establecido el margen izquierdo en la etiqueta en la celda #1 para que se alinee con la etiqueta en la celda #2. Todo se ve bien en un iPhone, pero si muestro la misma tabla en un iPad donde el tamaño del contenedor de la vista de tabla es la mitad del tamaño de la pantalla, la celda #2 obtiene más margen (dinámicamente?) mientras que la celda #1 mantiene el margen absoluto que establecí en las restricciones. También traté de cambiar el margen izquierdo en la celda #1 con el atributo "relativo al margen" pero a no aprovechar.
IPhone:
IPad (con ancho de vista de tabla = 1/2 tamaño de pantalla)
Entonces la pregunta es: ¿Cómo establezco las restricciones para la etiqueta en la celda #1 para que se alinee como la celda #2?
Aquí también hay un enlace a un proyecto de ejemplo de Xcode 6.1 que demuestra el problema. Ejecutar en iPhone y iPad para ver la diferencia:
Https://dl.dropboxusercontent.com/u/5252156/Code/tableViewTest.zip
Esta pregunta podría ser relacionado con Layout static table cell para iPhone y iPad, pero también podría diferir para iOS 8 ya que se supone que todo es adaptable ahora. Es por eso que decidí publicar esta pregunta de todos modos.
5 answers
Cómo arreglarlo
Después de luchar con el equipo de informes de errores de Apple con muchos proyectos de muestra y capturas de pantalla y diseccionar esa respuesta , he encontrado que la solución para que sus celdas de estilo personalizado se comporten consistentemente con respecto a sus márgenes y sean como las celdas UITableViewCells predeterminadas, debe hacer lo siguiente (principalmente basado en la respuesta de Becky, he destacado lo que es diferente y lo que lo hizo funcionar para mí):
- Seleccione la celda vista de contenido en IB
- Vaya al Inspector de Tamaño
-
En la sección Márgenes de diseño, marque Conservar márgenes de Supervisión (no haga clic en el signo más)
-
(Y aquí está la clave) Haga lo mismo para la celda en sí (el padre de la vista de contenido si se quiere)
-
Configure sus restricciones de la siguiente manera : Etiqueta.Leading = Superview.Margen inicial (con un constante de 0)
¡Ahora todas sus celdas tendrán su etiqueta consistente con las celdas predeterminadas! Esto funciona para mí en Xcode 7 y arriba e incluye la solución mencionada en el hilo al que me referí. IB y el simulador deben mostrar ahora etiquetas correctamente alineadas.
También podría hacer algo de esto programáticamente, por ejemplo en la clase del Controlador de vista :
cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true
O usted podría tener que configurar por llamando a UIAppearance una vez al inicio (solo conozco Swift, lo siento) :
UITableViewCell.appearance().preservesSuperviewLayoutMargins = true
UITableViewCell.appearance().contentView.preservesSuperviewLayoutMargins = true
Cómo y por qué funciona
Como Ethan amablemente señaló, la propia documentación de Apple en UIView describe preservesSuperviewLayoutMargins
de la siguiente manera:
Cuando el valor de esta propiedad es
true
, los márgenes de la superview también se consideran cuando se presenta el contenido. Este margen afecta a los diseños en los que la distancia entre el borde de una vista y su vista superior es menor que el margen correspondiente. Por ejemplo, es posible que tenga una vista de contenido cuyo marco coincida exactamente con los límites de su supervisión. Cuando cualquiera de los márgenes de la vista superior está dentro del área representada por la vista de contenido y sus propios márgenes, UIKit ajusta el diseño de la vista de contenido para respetar los márgenes de la vista superior. La cantidad del ajuste es la cantidad más pequeña necesaria para asegurar que el contenido también esté dentro de los márgenes del superview.
Por lo tanto, si desea que el contenido de su celda se alinee con el Los márgenes de tableView (es bisabuelo si lo desea), necesita que los dos ascendentes de su contenido, la Vista de contenido y la Celda de la tabla en sí, conserven los márgenes de su propia supervisión.
Por qué esto no es un comportamiento predeterminado me sorprende : siento que la mayoría de los desarrolladores que no quieren personalizar todo esperarían esta "herencia" por defecto.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:24
Me encontré con el mismo problema que tú y se me ocurrió una solución.
Primero, un poco de fondo: Desde iOS 8, las celdas de vista de tabla predeterminadas respetan los layoutMargins
de la celda para adaptarse a diferentes rasgos (también conocidas como pantallas, también conocidas como dispositivos). Por ejemplo, los márgenes de diseño en todos los iPhones (excepto iPhone 6 Plus cuando se muestra en una hoja de formulario) son {8, 16, 8, 16}
. En iPad son {8, 20, 8, 20}
. Así que ahora sabemos que hay 4 píxeles de diferencia, que lo más probable es que su celda de vista de tabla personalizada no respete.
Tu vista de tabla la subclase cell necesita adaptar la restricción de margen izquierdo cuando los layoutMargins cambian.
Aquí está el fragmento de código relevante:
- (void)layoutMarginsDidChange
{
[super layoutMarginsDidChange];
self.leftLayoutMarginConstraint.constant = self.layoutMargins.left;
self.rightLayoutMarginConstraint.constant = self.layoutMargins.right;
}
Adaptarse a los márgenes de diseño en el código le permite obtener siempre el relleno adecuado para su etiqueta de título.
También puede echar un vistazo a una de mis subclases UITableViewCell que ya respetan layoutMargins: https://github.com/bhr/BHRExtensions/blob/master/BHRExtensions/Utilities/BHRTitleAndValueTableCell.m
Salud
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-05 11:27:25
Después de leer las respuestas existentes y no encontrar una solución programática obvia, investigué un poco más y ahora tengo una buena respuesta para cualquier otra persona que enfrente este problema.
En primer lugar, no es necesario establecer preservesSuperviewLayoutMargins
a la vista de la celda o la vista de contenido como otro las respuestas implican. Si bien el valor predeterminado es false
, cambiarlo a true
no tuvo ningún efecto notable que pudiera ver.
La clave para hacer que esto realmente funcione es la propiedad layoutMarginsGuide
el UIView
. Usando este valor, podemos simplemente fijar fácilmente el leadingAnchor
de cualquier subview al leadingAnchor
de la guía. Así es como se ve en el código (y muy bien puede ser lo que IB está haciendo detrás de escena como en respuesta de Jonas).
En una subclase UITableViewCell
, harías algo como esto: {[18]]}
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
let leading = margins.leadingAnchor
subview1.leadingAnchor.constraintEqualToAnchor(leading).active = true
subview2.leadingAnchor.constraintEqualToAnchor(leading).active = true
super.updateConstraints()
}
Actualización de Swift 4.1
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
let leading = margins.leadingAnchor
subview1.leadingAnchor.constraint(equalTo: leading).isActive = true
subview2.leadingAnchor.constraint(equalTo: leading).isActive = true
super.updateConstraints()
}
Eso es todo! Si está desarrollando para las versiones de iOS anteriores a iOS 9, deberá sustituir los anclajes de diseño y usar el recuadro layoutMargins
en su lugar.
Nota: Escribí una biblioteca para hacer el anclaje un poco más bonito, si prefieres una sintaxis más limpia. Se llama SuperLayout y está disponible en Cocoapods. En la parte superior de tu archivo fuente, importa SuperLayout
:
import SuperLayout
Y luego en su bloque de diseño, use ~~
, ≤≤
, y ≥≥
para fijar restricciones:
override func updateConstraints() {
let margins = contentView.layoutMarginsGuide
subview1.leadingAnchor ~~ margins.leadingAnchor
subview2.leadingAnchor ~~ margins.leadingAnchor
super.updateConstraints()
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-09 12:33:11
Pude obtener celdas con estilos personalizados alineados con las celdas estándar haciendo lo siguiente:
- En el Esquema del documento, seleccione la" Vista de contenido " para la celda con el estilo personalizado.
- Vaya al Inspector de Tamaño.
- En el menú desplegable "Márgenes de diseño", pulse el símbolo más pequeño junto a "Preservar márgenes de Supervisión."
- Seleccione la clase de tamaño del iPad, que es "Ancho regular x Altura regular."
- Marque la casilla de verificación junto a " Preservar Supervisión Margen."
- Resuelva cualquier advertencia de Diseño automático actualizando los marcos.
Esto funcionó para mí en Xcode 7; espero que también funcione en Xcode 6.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-22 22:19:46
Tuve este problema al probar en un iPad Air, OS 10.1.1. Los encabezados de las tablas estaban sangrados mucho más lejos de lo que deberían haber sido, y era aún peor en la orientación horizontal. Pero estaban bien en iphones hasta OS 11.
La solución sorprendente fue la siguiente línea de código, justo después de que se creó la tabla (Lo siento, solo trabajo en C#, pero es fácil calcular los equivalentes Obj-C y Swift):
myTableView.SeparatorInset = myTableView.SeparatorInset;
Entonces todo estaba sangrando como debería ser!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-26 11:17:22