Content-Hugging vs Compression-Resistance

Content-Hugging vs Compression-Resistance

Las constraints tienen prioridades que usamos para indicar que constraints son más importantes que otras. Con las prioridades ayudamos al sistema a decidir como debe configurar las vistas en la UI.

SwiftBeta

Muchas de las vistas que usamos en UIKit tienen un intrinsic content size. El intrinsic content size es el tamaño idóneo para que una vista pueda representar la información que contiene, puede ser en un UILabel, UITextView, UIImageView, etc.

Por ejemplo en un UILabel la vista crece según el tamaño del texto que hayas asignado. Por eso, podemos representar un UILabel especificando las coordenadas X e Y. Al asignar las coordenadas X e Y, autolayout sabe que tamaño debe tener esa vista con el intrinsic content size.

¿Por qué te cuento esto? Hay aveces que tenemos varias subvistas al mismo nivel y tenemos que dar prioridades. Con esto conseguirás que una vista tenga más prioridad para expandirse o menos prioridad para contraerse.

Content Hugging

Esta prioridad hará que la vista se resista para que no se expanda más de su intrinsic content size. Cuanto mayor sea el valor que le demos a esta prioridad, más alta será la prioridad para que no se expanda.

Vamos a usar el código que puedes descargarte de nuestro Github y que vimos en anteriores posts de Autolayout:

Autolayout por código en Swift
Para crear una aplicación con UIKit en Swift debes conocer cómo colocar las vistas en la pantalla y para eso necesitas usar las constraints. En este caso explicamos constraints por código.

Vamos a añadir dos UILabels, uno a continuación del otro:

let descriptionLeftLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.text = "Aprende a programar"
    label.backgroundColor = .green
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()
    
let descriptionRightLabel: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.text = "Swift desde cero"
    label.backgroundColor = .yellow
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
}()

Estas nuevas vistas las añadiremos al container que teníamos ya creado:

 containerView.addSubview(descriptionLeftLabel)
 containerView.addSubview(descriptionRightLabel)

y añadiremos estas constraints:

NSLayoutConstraint.activate([
    descriptionLeftLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8),
    descriptionLeftLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 8),
    
    
    descriptionLeftLabel.trailingAnchor.constraint(equalTo: descriptionRightLabel.leadingAnchor),
    
    descriptionRightLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8),
    descriptionRightLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 8),
])

Si compilamos la aplicación, vemos que tenemos un resultado como la siguiente figura:

Los dos UILabels tienen la misma prioridad para no expandirse. Pero, ¿qué pasa si queremos que el UILabel con fondo verde ocupe más espacio que el UILabel con fondo amarillo? Tenemos que dar más prioridad al UILabel amarillo, queremos que solo ocupe su intrinsic content size y no se expanda. Para hacer la prueba, vamos a cambiar estas prioridades:

// Prioridad baja en content hugging en el eje horizontal
descriptionLeftLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
// Prioridad alta en content hugging en el eje horizontal
descriptionRightLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)

El código que acabamos de ver hace que nuestra aplicación ahora tenga el siguiente resultado:

Justo lo que queríamos. Ahora vamos a ver otra propiedad muy útil cuando usamos constraints.

Compression Resistance

Esta prioridad hará que la vista se resista a encogerse más que su intrinsic content size. Cuanto mayor sea el valor que le demos a esta prioridad, más alta será la prioridad para que no se encoja.

En este ejemplo vamos a crear nuestro UIButton al containerView:

let clickButton: UIButton = {
    let button = UIButton()
    button.translatesAutoresizingMaskIntoConstraints = false
    button.setTitle("Tap me please!", for: .normal)
    return button
}()

y lo vamos a añadir al containerView:

containerView.addSubview(clickButton)

let widthConstaint = clickButton.widthAnchor.constraint(equalToConstant: 60)

NSLayoutConstraint.activate([
    clickButton.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
    clickButton.centerYAnchor.constraint(equalTo: containerView.centerYAnchor),
    widthConstaint
])

El resultado es un botón con el texto demasiado largo y por eso no se puede leer todo el texto:

Para arreglarlo vamos a:

  • Subir la prioridad del Compression Resistance.
  • Bajar la prioridad del widthAnchor.

Solo añadiendo estás dos líneas de código, conseguimos que la vista se comporte tal y como queremos:

widthConstaint.priority = .defaultLow
clickButton.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)

Hasta aquí el post de hoy, gracias por leernos! 🤓
Si tienes preguntas no dudes en contactar con nosotros a través de Twitter

Si quieres seguir aprendiendo sobre SwiftUI, Swift, Xcode, o cualquier tema relacionado con el ecosistema Apple


UIKit