Etiqueta multilínea en UIStackView

154

Cuando se coloca la etiqueta multilínea (con el salto de línea establecido en Word Wrap) en una vista de pila, la etiqueta pierde inmediatamente el salto de línea y muestra el texto de la etiqueta en una línea.

¿Por qué sucede esto y cómo se conserva la etiqueta multilínea dentro de una vista de pila?

Bendición
fuente
1
¿Estableció el número de líneas en 0?
dasdom
¿Encontraste una respuesta?
Brenden

Respuestas:

204

La respuesta correcta está aquí:
https://stackoverflow.com/a/43110590/566360


  1. Incrustar el UILabelinterior de un UIView(Editor -> Incrustar en -> Ver)
  2. Utilice restricciones para ajustar el UILabela la UIView(por ejemplo, espacio final, espacio superior y espacio inicial para supervisar restricciones)

Se UIStackViewestirará UIViewpara que se ajuste correctamente y UIViewrestringirá las UILabellíneas múltiples.

Andy
fuente
36
¡Es tan estúpido pero funciona! Gracias hombre ! Tal vez algún día Apple lanzará algo que no esté medio roto en su SDK ...
Guillaume L.
1
¡Oh hombre! Eres un salvador! Había estado en eso durante horas :) Recuerda también establecer las líneas en 0 en el Inspector de atributos, me faltaba eso.
absin
8
esta es la solución, la solución correcta está aquí stackoverflow.com/a/43110590/566360
vietstone
2
Fue suficiente para mí simplemente cambiar la alineación horizontal al centro. No hay UIViewtravesuras necesarias.
cuña
1
@Lucien esto es exactamente lo que se dijo en esta respuesta. pero la respuesta correcta está aquí: stackoverflow.com/a/43110590/566360
Andy
144

Para una vista de pila horizontal que tiene una UILabelde sus vistas, en Interface Builder se establece en primer lugar label.numberOfLines = 0. Esto debería permitir que la etiqueta tenga más de 1 línea. Inicialmente, esto no funcionó para mí cuando la vista de la pila tenía stackView.alignment = .fill. Para que funcione, simplemente configúrelo stackView.alignment = .center. La etiqueta ahora puede expandirse a varias líneas dentro de UIStackView.

La documentación de Apple dice

Para todas las alineaciones, excepto la alineación de relleno, la vista de pila utiliza la propiedad de Tamaño de contenido intrínseco de cada vista organizada al calcular su tamaño perpendicular al eje de la pila

Tenga en cuenta la palabra, excepto aquí. Cuando .fillse usa, la horizontal UIStackViewNO cambia de tamaño verticalmente usando los tamaños de subvistas organizados.

pbm
fuente
3
Esta es la solución adecuada sin hacks. ¡Leer los documentos siempre vale la pena!
Islam Q.
3
Esta debería ser la solución aceptada. Con la solución alternativa de UIView, el texto de UILabel flotó sobre otra subvista en mi caso.
14
En realidad no funciona ... Tengo 3 etiquetas en mi stackview. He establecido líneas a cero y alineación al centro ... Todavía veo que las etiquetas están cortadas.
MatterGoal
11
Es .alignmentel UIStackView que debes configurar en cualquier cosa que .fill no sea UILabel, lo cual es un poco confuso en el ejemplo dado label.alignment = .center. Creo que esto debería serstackView.alignment = .center
ae14
2
Pero, ¿cómo puedo hacer que un UILableView de varias líneas funcione en StackView vertical?
DàChún
42
  • Primero establezca el número de etiqueta de líneas en 0
  • La vista de pila aún no crecerá a multiLinemenos que le des un ancho fijo. Cuando arreglamos su ancho, se rompe a multilínea cuando se alcanza ese ancho como se muestra:

grabación de pantalla

Si no le damos un ancho fijo a la vista de la pila, las cosas se vuelven ambiguas. ¿Cuánto tiempo crecerá la vista de pila con la etiqueta (si el valor de la etiqueta es dinámico)?

Espero que esto pueda solucionar tu problema.

Irfan
fuente
3
¿Qué sucede si el ancho de la vista de la pila no es fijo, sino que es proporcional al ancho del dispositivo (es decir, de la supervista)?
OutOnAWeekend
Sí, también podemos tener un ancho proporcional. El punto es si la etiqueta tiene que romperse después de cierto ancho, sea lo que sea, pero necesita ser definida.
Irfan
@AnandKumar puede especificar preferredMaxLayoutWidthen viewDidLayoutSubviewssu etiqueta en UIViewControllero en layoutSubviewssi subclase UIView.
Shyngys Kassymov
44
Esta debería haber sido la respuesta aceptada, el objetivo de la vista de pila es no tener que agregar restricciones entre la vista de pila y sus elementos ...
Miel
44
Dar un valor fijo derrota el propósito del autolayout
Islam Q.
17

Establecer PreferredMaxLayoutWidth en UILabel funcionó para mí

self.myLabel.preferredMaxLayoutWidth = self.bounds.size.width;
Pablo Martinez
fuente
3
Esta es la respuesta correcta en mi libro. Cuando alguna vez te metas con UILabel's y necesites manejar múltiples líneas, realmente necesitas darle un ancho máximo de preferencia. Puede funcionar sin él, pero las cosas pueden ponerse bastante funky sin él.
Mof
2
Todos intentaron todas las respuestas y todas estaban equivocadas excepto la suya. ¡Gracias!
Levan
Esto resolvió perfectamente mi problema y se siente mucho menos hacky, gracias.
Ethan Zhao
¡Gracias por tu respuesta! En mi caso, la alineación de mi stackView ya estaba establecida .centery realmente no necesitaba mi etiqueta dentro de UIView.
Fariza Zhumat
12

Simplemente establezca el número de líneas en 0 en el inspector de atributos para la etiqueta. Funcionará para ti.

ingrese la descripción de la imagen aquí

technerd
fuente
12
Para un stackView vertical, debe tener la distribución establecida en "Rellenar" para que esto funcione.
SnoopyProtocol
2
Establecer la distribución en "Rellenar" es la clave para la solución.
Dylan Moore el
Descubrí que para un UIStackView vertical, tenía que establecer restricciones superiores e inferiores para que un UILabel secundario se expandiera y mostrara todo el texto.
bruce1337
8

Después de probar todas las sugerencias anteriores, no encontré ningún cambio de propiedades para UIStackView. Solo cambio las propiedades de los UILabels de la siguiente manera (las etiquetas ya se han agregado a una vista de pila vertical):

Swift 4 ejemplo:

    [titleLabel, subtitleLabel].forEach(){
        $0.numberOfLines = 0
        $0.lineBreakMode = .byWordWrapping
        $0.setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical)
    }
Bill Chan
fuente
5

El truco de magia para mí fue establecer un widthAnchora UIStackView.

Configuración leadingAnchory trailingAnchorno funcionará, pero la configuración centerXAnchore widthAnchorhizo que UILabel se muestre correctamente.

Skoua
fuente
4

iOS 9+

Llame [textLabel sizeToFit]después de configurar el texto de UILabel.

sizeToFit rediseñará la etiqueta multilínea con el ancho máximo preferido. La etiqueta cambiará el tamaño de stackView, que cambiará el tamaño de la celda. No se requieren restricciones adicionales además de fijar la vista de pila a la vista de contenido.

aumansoftware
fuente
4

Aquí hay un ejemplo completo de una vertical UIStackViewcompuesta de líneas UILabelmúltiples con altura automática.

Las etiquetas se ajustan según el ancho de la vista de pila y la altura de la vista de pila se basa en la altura de la etiqueta. (Con este enfoque no necesita incrustar las etiquetas en a UIView.) (Swift 5, iOS 12.2)

ingrese la descripción de la imagen aquí

// A vertical stackview with multiline labels and automatic height.
class ThreeLabelStackView: UIStackView {
    let label1 = UILabel()
    let label2 = UILabel()
    let label3 = UILabel()

    init() {
        super.init(frame: .zero)
        self.translatesAutoresizingMaskIntoConstraints = false
        self.axis = .vertical
        self.distribution = .fill
        self.alignment = .fill

        label1.numberOfLines = 0
        label2.numberOfLines = 0
        label3.numberOfLines = 0
        label1.lineBreakMode = .byWordWrapping
        label2.lineBreakMode = .byWordWrapping
        label3.lineBreakMode = .byWordWrapping
        self.addArrangedSubview(label1)
        self.addArrangedSubview(label2)
        self.addArrangedSubview(label3)

        // (Add some test data, a little spacing, and the background color
        // make the labels easier to see visually.)
        self.spacing = 1
        label1.backgroundColor = .orange
        label2.backgroundColor = .orange
        label3.backgroundColor = .orange
        label1.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi."
        label2.text = "Hello darkness my old friend..."
        label3.text = "When I wrote the following pages, or rather the bulk of them, I lived alone, in the woods, a mile from any neighbor, in a house which I had built myself, on the shore of Walden Pond, in Concord, Massachusetts, and earned my living by the labor of my hands only."
    }

    required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}

Aquí hay una muestra ViewControllerque lo usa.

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myLabelStackView = ThreeLabelStackView()
        self.view.addSubview(myLabelStackView)

        // Set stackview width to its superview.
        let widthConstraint  = NSLayoutConstraint(item: myLabelStackView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1, constant: 0)
        self.view.addConstraints([widthConstraint])
    }
}
zekel
fuente
3

Agregar UIStackViewpropiedades,

stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.spacing = 8.0
stackView.axis = .horizontal

En lugar de agregar una etiqueta dentro de la UIViewcual no es necesario. Si está usando adentro UITableViewCell, vuelva a cargar los datos en la rotación.

Sagar Daundkar
fuente
3

La siguiente es una implementación de Playground de una etiqueta de varias líneas con un salto de línea dentro de a UIStackView. No requiere incrustar UILabelnada en el interior y ha sido probado con Xcode 9.2 y Swift 4. Espero que sea útil.

import UIKit
import PlaygroundSupport

let containerView = UIView()
containerView.frame = CGRect.init(x: 0, y: 0, width: 400, height: 500)
containerView.backgroundColor = UIColor.white

var label = UILabel.init()
label.textColor = .black
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "This is an example of sample text that goes on for a long time. This is an example of sample text that goes on for a long time."

let stackView = UIStackView.init(arrangedSubviews: [label])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .fill
containerView.addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
stackView.widthAnchor.constraint(equalTo: containerView.widthAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true

PlaygroundPage.current.liveView = containerView
JaredH
fuente
1

El diseño del sistema debe determinar el origen, el ancho y la altura para dibujar sus subvistas, en este caso todas sus subvistas tienen la misma prioridad, ese punto genera conflicto, el sistema de diseño no conoce las dependencias entre las vistas, cuál dibuja primero, segundo, etc.

Establecer compresión de subvistas de pila resolverá el problema con varias líneas, dependiendo de que su vista de pila sea horizontal o vertical y cuál desea que se convierta en varias líneas. stackOtherSubviews .setContentCompressionResistancePriority(.defaultHight, for: .horizontal) lblTitle.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

Trung Phan
fuente
0

En mi caso, seguí las sugerencias anteriores, pero mi texto seguía truncado en una sola línea, aunque solo en horizontal. Resulta que encontré un \0personaje nulo invisible en el texto de la etiqueta que era el culpable. Debe haberse introducido junto con el símbolo del guión que había insertado. Para ver si esto también está sucediendo en su caso, use Ver depurador para seleccionar su etiqueta e inspeccionar su texto.

Jordan H
fuente
0

Xcode 9.2:

Simplemente establezca el número de líneas en 0. Storyboard se verá extraño después de configurar esto. No te preocupes, ejecuta el proyecto. Mientras se ejecuta, todo cambiará de tamaño de acuerdo con la configuración de su guión gráfico. Creo que es un tipo de error en el guión gráfico. ingrese la descripción de la imagen aquí

Consejos: establezca "número de revestimiento en 0" en el último. reinícielo cuando desee editar alguna otra vista y configúrelo en 0 después de la edición.

ingrese la descripción de la imagen aquí

Surendra Kumar
fuente
0

¡Qué funcionó para mí!

stackview: alineación: relleno, distribución: relleno, restricción ancho proporcional a la supervista ej. 0.8,

etiqueta: centro y líneas = 0

Michał Ziobro
fuente
0

Para cualquiera que todavía no pueda hacer que funcione. Intente configurar Autoshrink con una Escala de fuente mínima en ese UILabel.

Captura de pantalla de la configuración de UILabel Autoshrink

Michal Mondík
fuente
Considere mejorar su respuesta con un ejemplo de código.
Fridoo
1
@fridoo Se agregó una captura de pantalla de la configuración.
Michal Mondík
0

Es casi como la respuesta de @ Andy, pero puedes agregar tu trabajo UILabelextra UIStackviewy vertical para mí.

Zaporozhchenko Oleksandr
fuente
0

Para mí, el problema era que la altura de la vista de la pila era simplemente demasiado corta. La vista de la pila y la etiqueta se configuraron correctamente para permitir que la etiqueta tenga varias líneas, pero la etiqueta fue la primera víctima de la compresión de contenido que la vista de la pila utilizó para que su altura fuera lo suficientemente pequeña.

Luke
fuente