Etiqueta multilinha no UIStackView

154

Ao colocar o rótulo com várias linhas (com quebra de linha definido como Quebra de linha) em uma exibição de pilha, o rótulo imediatamente perde o quebra de linha e exibe o texto do rótulo em uma linha.

Por que isso está acontecendo e como preservar o rótulo de várias linhas em uma exibição de pilha?

Boon
fonte
1
Você definiu o número de linhas para 0?
dasdom
Você encontrou uma resposta?
Brenden

Respostas:

204

A resposta correta está aqui:
https://stackoverflow.com/a/43110590/566360


  1. Incorporar o UILabelinterior a UIView(Editor -> Incorporar -> Exibir)
  2. Use restrições para ajustá-las UILabelàs UIView(por exemplo, espaço à direita, espaço superior e espaço à frente das restrições da superview)

O UIStackViewesticará o UIViewpara encaixar corretamente e o UIViewrestringirá a UILabelvárias linhas.

Andy
fonte
36
É tão estúpido, mas funciona! Valeu cara! Talvez um dia a Apple vai lançar algo que não é metade quebrado em seu SDK ...
Guillaume L.
1
Oh cara! Você é um salvador! Eu estava nisso há horas :) Lembre-se de também definir as linhas como 0 no Attributes Inspector, estava faltando exatamente isso.
absin
8
esta é solução, a solução correta é aqui stackoverflow.com/a/43110590/566360
vietstone
2
Foi suficiente para mim apenas mudar o alinhamento horizontal para o centro. Não são UIViewnecessárias travessuras.
shim
1
@ Lucien, é exatamente o que foi dito nesta resposta. mas a resposta correta está aqui: stackoverflow.com/a/43110590/566360
Andy
144

Para uma exibição de pilha horizontal que tenha uma UILabelcomo uma de suas exibições, no Interface Builder primeiro definido label.numberOfLines = 0. Isso deve permitir que o rótulo tenha mais de 1 linha. Inicialmente, isso não funcionou para mim quando a exibição da pilha funcionou stackView.alignment = .fill. Para fazê-lo funcionar, basta definir stackView.alignment = .center. O rótulo agora pode ser expandido para várias linhas dentro do UIStackView.

A documentação da Apple diz

Para todos os alinhamentos, exceto o alinhamento de preenchimento, a visualização da pilha usa a propriedade intrínseca Tamanho de Conteúdo de cada visualização organizada ao calcular seu tamanho perpendicular ao eixo da pilha

Observe a palavra, exceto aqui. Quando .fillé usada, a horizontal UIStackViewNÃO se redimensiona verticalmente usando os tamanhos das subvisões organizadas.

pbm
fonte
3
Esta é a solução adequada sem hacks. A leitura dos documentos sempre compensa!
Islam Q.
3
Essa deve ser a solução aceita. Com a solução alternativa do UIView, o texto do UILabel flutuou sobre outra subvisão no meu caso.
14
Na verdade, ele não funciona ... Eu tenho três rótulos no meu stackview. Coloquei as linhas em zero e o alinhamento no centro ... Continuo vendo as etiquetas cortadas.
MatterGoal
11
É o .alignmentdo UIStackView que você tem que pronto para qualquer coisa, mas .fill não o UILabel, que é um pouco confuso no exemplo dado label.alignment = .center. Eu acho que isso deveria serstackView.alignment = .center
ae14 25/09
2
Mas como posso fazer com que várias linhas UILableView funcionem no StackView vertical?
DàChún 11/04
42
  • Primeiro defina o número de linhas da etiqueta como 0
  • A visualização da pilha ainda não aumentará, a multiLinemenos que você tenha uma largura fixa. Quando fixamos sua largura, ela é quebrada para várias linhas quando essa largura é atingida, conforme mostrado:

gravação de tela

Se não atribuirmos uma largura fixa à exibição da pilha, as coisas ficarão ambíguas. Quanto tempo a exibição da pilha crescerá com o rótulo (se o valor do rótulo for dinâmico)?

Espero que isso possa resolver o seu problema.

Irfan
fonte
3
E se a largura da exibição da pilha não for fixa, mas for proporcional à largura do dispositivo (ou seja, da superview).
OutOnAWeekend
Sim, também podemos ter uma largura proporcional. A questão é se o rótulo precisa quebrar após alguma largura, seja qual for, mas precisa ser definido.
Irfan
@AnandKumar você pode especificar preferredMaxLayoutWidthno viewDidLayoutSubviewspara a sua etiqueta em UIViewControllerou layoutSubviewsse você subclasse UIView.
Shyngys Kassymov 02/09
4
Este deve ter sido a resposta aceita, todo o ponto do stackview é para não ter de adicionar restrições entre a stackview e seus elementos ...
Mel
4
Atribuir um valor fixo anula o objetivo do autolayout
Islam Q.
17

A configuração do preferencialMaxLayoutWidth para o UILabel funcionou para mim

self.myLabel.preferredMaxLayoutWidth = self.bounds.size.width;
Pablo Martinez
fonte
3
Esta é a resposta correta no meu livro. Sempre que você mexe com o UILabel e precisa lidar com várias linhas, realmente precisa dar a ele umMaxLayoutWidth preferido. Pode funcionar sem ele, mas as coisas podem ficar um pouco estranhas muito rapidamente sem ele.
Mof
2
Todos tentaram todas as respostas e todas estavam erradas, exceto a sua. Obrigado!
levan
Isso resolveu perfeitamente meu problema e parece muito menos invasivo, obrigado.
Ethan Zhao
Obrigado pela sua resposta! No meu caso, o alinhamento do meu stackView já estava definido .centere eu realmente não precisava da minha etiqueta dentro do UIView.
Fariza Zhumat
12

Basta definir o número de linhas como 0 no Inspetor de atributos para a etiqueta. Isso funcionará para você.

insira a descrição da imagem aqui

technerd
fonte
12
Para um stackView vertical, você precisa ter a Distribuição definida como "Preencher" para que isso funcione.
SnoopyProtocol
2
Definir a distribuição como "Preenchimento" é a chave para a correção.
Dylan Moore
Eu descobri que para um UIStackView vertical, eu tinha que definir restrições superior e inferior para que um filho UILabel expandisse e mostrasse todo o texto.
bruce1337
8

Depois de tentar todas as sugestões acima, não encontrei nenhuma alteração de propriedades necessária para o UIStackView. Acabei de alterar as propriedades dos UILabels da seguinte forma (os rótulos já foram adicionados a uma exibição de pilha vertical):

Exemplo do Swift 4:

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

O truque de mágica para mim foi definir um widthAnchorpara o UIStackView.

Definir leadingAnchore trailingAnchornão funcionará, mas definir centerXAnchore widthAnchorexibir o UILabel corretamente.

Skoua
fonte
4

iOS 9 ou superior

Ligue [textLabel sizeToFit]depois de definir o texto do UILabel.

sizeToFit redimensionará o rótulo de múltiplas linhas usando o preferencialMaxWidth. O rótulo redimensionará o stackView, que redimensionará a célula. Não são necessárias restrições adicionais além de fixar a exibição de pilha na exibição de conteúdo.

aumansoftware
fonte
4

Aqui está um exemplo completo de uma vertical UIStackViewcomposta de multilinhas UILabels com altura automática.

O empacotamento dos rótulos é baseado na largura do stackview e a altura do empilhamento é baseada na altura do empacotamento do rótulo. (Com essa abordagem, você não precisa incorporar os rótulos em um UIView.) (Swift 5, iOS 12.2)

insira a descrição da imagem aqui

// 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") }
}

Aqui está um exemplo ViewControllerque o 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
fonte
3

Adicione UIStackViewpropriedades,

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

Em vez de adicionar uma etiqueta interna UIViewque não é necessária. Se você estiver usando o interior UITableViewCell, recarregue os dados na rotação.

Sagar Daundkar
fonte
3

A seguir, uma implementação do Playground do rótulo de várias linhas com uma quebra de linha dentro de a UIStackView. Ele não requer a incorporação de UILabelnada interno e foi testado com o Xcode 9.2 e o Swift 4. Espero que seja ú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
fonte
1

O layout do sistema deve descobrir a origem, a largura e a altura para desenhar as sub-visões. Nesse caso, todas as suas sub-visões têm a mesma prioridade; esse ponto cria conflito, o sistema de layout não conhece dependências entre as visões, qual delas desenha primeiro, segundo e assim por diante.

A compactação de subvisões de pilha definidas resolverá o problema com várias linhas, dependendo da exibição da pilha ser horizontal ou vertical e qual você deseja que se torne várias linhas. stackOtherSubviews .setContentCompressionResistancePriority(.defaultHight, for: .horizontal) lblTitle.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

Trung Phan
fonte
0

No meu caso, segui as sugestões anteriores, mas meu texto ainda estava sendo truncado em uma única linha, embora apenas na paisagem. Acontece que eu encontrei um \0caractere nulo invisível no texto do rótulo, que foi o culpado. Ele deve ter sido introduzido ao lado do símbolo em dash que eu havia inserido. Para ver se isso também está acontecendo no seu caso, use o View Debugger para selecionar seu rótulo e inspecionar seu texto.

Jordan H
fonte
0

Xcode 9.2:

Apenas defina o número de linhas como 0. O Storyboard ficará estranho depois de definir isso. Não se preocupe, execute o projeto. Durante a execução, tudo será redimensionado de acordo com a configuração do seu storyboard. Eu acho que é o tipo de bug no storyboard. insira a descrição da imagem aqui

Dicas: Defina "número de liner como 0" no último. redefina-o quando desejar editar outra exibição e defina como 0 após a edição.

insira a descrição da imagem aqui

Surendra Kumar
fonte
0

O que funcionou para mim!

stackview: alinhamento: preenchimento, distribuição: preenchimento, restrição de largura proporcional ao superview ex. 0.8,

label: center e lines = 0

Michał Ziobro
fonte
0

Para quem ainda não pode fazê-lo funcionar. Tente definir o Autoshrink com uma escala de fonte mínima nesse UILabel.

Configurações de captura automática de tela UILabel

Michal Mondík
fonte
Considere melhorar sua resposta com um exemplo de código.
fridoo 11/02/19
1
@fridoo Adicionada uma captura de tela das configurações.
Michal Mondík 12/02/19
0

É quase como a resposta de Andy, mas você pode adicionar o seu trabalho UILabelextra UIStackviewvertical para mim.

Zaporozhchenko Oleksandr
fonte
0

Para mim, o problema era que a altura da exibição da pilha era simplesmente muito curta. A exibição do rótulo e da pilha foi configurada corretamente para permitir que o rótulo tivesse várias linhas, mas o rótulo foi a primeira vítima da compactação de conteúdo usada pela exibição da pilha para obter sua altura suficientemente pequena.

Lucas
fonte