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:

  1. Celdas con estilo personalizado y
  2. 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:

Tabla en iPhone

IPad (con ancho de vista de tabla = 1/2 tamaño de pantalla)

Tabla en iPad

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.

Author: Community, 2014-12-11

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

  1. Seleccione la celda vista de contenido en IB
  2. Vaya al Inspector de Tamaño
  3. En la sección Márgenes de diseño, marque Conservar márgenes de Supervisión (no haga clic en el signo más)

    Comprobación de preservar márgenes de supervisión

  4. (Y aquí está la clave) Haga lo mismo para la celda en sí (el padre de la vista de contenido si se quiere)

    La celda y no la vista de contenido esta vez

  5. Configure sus restricciones de la siguiente manera : Etiqueta.Leading = Superview.Margen inicial (con un constante de 0)

    Establecer la restricción para la etiqueta de la celda personalizada

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

Resultado Final en el simulador

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.

 134
Author: Jonas Zaugg,
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

 6
Author: tubtub,
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()
}
 4
Author: Dan Loewenherz,
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:

  1. En el Esquema del documento, seleccione la" Vista de contenido " para la celda con el estilo personalizado.
  2. Vaya al Inspector de Tamaño.
  3. 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."
  4. Seleccione la clase de tamaño del iPad, que es "Ancho regular x Altura regular."
  5. Marque la casilla de verificación junto a " Preservar Supervisión Margen."
  6. 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.

 1
Author: Becky Hansmeyer,
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!

 0
Author: diyaddict,
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