iOS: UILabel de várias linhas no layout automático

183

Estou com problemas para tentar obter um comportamento de layout muito básico com o Layout automático. Meu controlador de exibição fica assim no IB:

insira a descrição da imagem aqui

O rótulo superior é o título, não sei quantas linhas serão. Eu preciso do rótulo do título para exibir todas as linhas de texto. Também preciso que os outros dois rótulos e a pequena imagem sejam dispostos logo abaixo do título, por mais altos que sejam. Eu defini restrições de espaçamento vertical entre os rótulos e a imagem pequena, bem como uma restrição de espaçamento superior entre o rótulo do título e sua superview e uma restrição de espaçamento inferior entre a imagem pequena e sua superview. O UIView branco não possui restrição de altura; portanto, ele deve se esticar verticalmente para conter suas subvisões. Eu configurei o número de linhas para o rótulo do título como 0.

Como posso redimensionar o rótulo do título para ajustar o número de linhas exigidas pela string? Meu entendimento é que não posso usar os métodos setFrame porque estou usando o Layout Automático. E eu tenho que usar o Layout automático, porque preciso que essas outras visualizações fiquem abaixo do rótulo do título (daí as restrições).

Como posso fazer isso acontecer?

James Harpe
fonte
3
Eu também estou lutando com um problema semelhante. Mas ainda estou lutando para que o rótulo superior ajuste dinamicamente sua altura para se ajustar ao conteúdo. Como você conseguiu isso?
jbandi
1
Por favor, considere marcar a resposta de @ mwhuss como a aceita.
precisa saber é o seguinte
1
Você alcançou o resultado necessário?
Aniruddha
verificar isso, você não precisa adicionar uma única linha de código stackoverflow.com/a/36862795/4910767
Badal Shah

Respostas:

254

O uso -setPreferredMaxLayoutWidthno UILabele autolayout deve lidar com o resto.

[label setPreferredMaxLayoutWidth:200.0];

Consulte a documentação do UILabel em preferidoMaxLayoutWidth .

Atualizar:

Só é necessário definir a heightrestrição no storyboard para Greater than or equal to, não é necessário definirPreferredMaxLayoutWidth.

mwhuss
fonte
8
Bom, enfim, fazer isso no construtor de interfaces?
Sjoerd Perfors
12
Defina também a restrição de altura no IB como Maior ou igual a ".
jowie
10
Lembrete: lembre-se de definir a propriedade numberOfLines.
Li Fumin 03/02
2
Sim, use o que você quiser que seja a largura máxima.
mwhuss
4
Consulte stackoverflow.com/a/29180323/1529675 para obter informações sobre como determinar preferredMaxLayoutWidthe ver devetc.org/code/2014/07/07/auto-layout-and-views-hat-wrap.html para obter uma descrição breve, mas informativa, completa com GIFs animados (não meu writeup, eu achei através do Google)
tboyce12
213

Expanda o número de linhas do conjunto de etiquetas para 0 e, mais importante, para a altura do conjunto de layout automático para> = x. O layout automático fará o resto. Você também pode conter seus outros elementos com base no elemento anterior para posicionar corretamente.

layout automático

Charlie Wu
fonte
Se esse método não funcionar para você, consulte a resposta de Cory Imdieke sobre como garantir que as visualizações subseqüentes não proíbem que o Auto Layout redimensione a (possivelmente) exibição de múltiplas linhas.
Tom Howard
1
Essa é a única resposta que funcionou para mim em uma célula de exibição de tabela. Também trabalha com um número fixo de linhas (em vez de 0 / ilimitado)
bgolson
Isso não funcionou para mim com um conjunto explícito de largura máxima preferencial, portanto, verifique se está definido como automático no IB ao usar esta técnica. Cheers
jmc 10/02
A configuração da largura máxima automática está disponível apenas no iOS8. Também, no meu entendimento, ajustará a altura automaticamente e, na minha experiência, você não precisa definir uma restrição de altura para que ela funcione. Isso funcionou para mim usando uma largura explícita.
25415 Tony
1
Isso não funcionou para mim. Acredito que stackoverflow.com/a/13032251/1529675 é a resposta correta.
precisa saber é o seguinte
48

Fonte: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html

Tamanho do conteúdo intrínseco do texto de várias linhas

O tamanho do conteúdo intrínseco do UILabel e NSTextField é ambíguo para o texto de várias linhas. A altura do texto depende da largura das linhas, que ainda será determinada ao resolver as restrições. Para resolver esse problema, ambas as classes têm uma nova propriedade chamada preferidoMaxLayoutWidth, que especifica a largura máxima da linha para calcular o tamanho do conteúdo intrínseco.

Como geralmente não conhecemos esse valor antecipadamente, precisamos adotar uma abordagem em duas etapas para fazer isso direito. Primeiro, deixamos o Auto Layout fazer seu trabalho e depois usamos o quadro resultante na passagem de layout para atualizar a largura máxima preferida e acionar o layout novamente.

- (void)layoutSubviews
{
    [super layoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [super layoutSubviews];
}

A primeira chamada para [super layoutSubviews] é necessária para o rótulo definir seu quadro, enquanto a segunda chamada é necessária para atualizar o layout após a alteração. Se omitirmos a segunda chamada, obtemos um erro NSInternalInconsistencyException, porque fizemos alterações na passagem do layout que exigem a atualização das restrições, mas não acionamos o layout novamente.

Também podemos fazer isso em uma subclasse de rótulo:

@implementation MyLabel
- (void)layoutSubviews
{
    self.preferredMaxLayoutWidth = self.frame.size.width;
    [super layoutSubviews];
}
@end

Nesse caso, não precisamos chamar [super layoutSubviews] primeiro, porque quando layoutSubviews é chamado, já temos um quadro no próprio rótulo.

Para fazer esse ajuste no nível do controlador de exibição, conectamos a viewDidLayoutSubviews. Nesse ponto, os quadros da primeira passagem de Layout automático já estão definidos e podemos usá-los para definir a largura máxima preferida.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

Por fim, verifique se você não possui uma restrição explícita de altura no rótulo que tenha uma prioridade mais alta que a prioridade de resistência à compactação de conteúdo do rótulo. Caso contrário, ele superará a altura calculada do conteúdo. Verifique todas as restrições que podem afetar a altura da etiqueta.

Anton Matosov
fonte
obrigado obrigado obrigado não apenas pelo código, mas, mais importante, pela explicação e pelos exemplos de como fazer isso não apenas em uma subclasse, mas também no controlador de exibição.
precisa
Estou tentando isso no meu controlador de exibição, e viewDidLayoutSubviews está sendo chamado após viewWillAppear e viewDidAppear. Após o viewDidAppear, há um flash quando os rótulos são redimensionados corretamente. existe alguma maneira de evitar isso? existe uma maneira de o redimensionamento ser concluído antes que o usuário veja alguma coisa? no meu caso, minhas etiquetas de várias linhas estão em uma tableHeaderView, e então eu também estou redimensionando a altura da vista de cabeçalho com base nos rótulos usando este exemplo ( stackoverflow.com/questions/20982558/... )
djibouti33
@ djibouti33, você pode fornecer o código de amostra mínimo (por exemplo, no formato de repositório do github) para que eu possa verificar facilmente o seu problema?
Anton Matosov 04/04
Obrigado @Anton. Desde então, nosso design mudou de um UITableView para um UICollectionView e, felizmente, não vi esse comportamento. Se eu puder acessar meu histórico do git para criar um pequeno projeto de amostra, eu o informarei.
djibouti33
Um problema para mim desde que comecei a rodar em um iOS 9 sim, mas quando testei no iOS 8, o problema começou. Preciso definir a largura máxima manualmente.
naz
17

Eu estava apenas lutando com esse cenário exato, mas com mais algumas visualizações necessárias para redimensionar e descer conforme necessário. Isso estava me deixando louco, mas eu finalmente descobri.

Aqui está a chave: o Interface Builder gosta de criar restrições extras à medida que você adiciona e move visualizações e você pode não perceber. No meu caso, eu tinha uma visão no meio do caminho que tinha uma restrição extra que especificava o tamanho entre ela e sua super visão, basicamente fixando-a nesse ponto. Isso significava que nada acima poderia ser redimensionado porque iria contra essa restrição.

Uma maneira fácil de saber se esse é o caso é tentar redimensionar o rótulo manualmente. IB deixa você crescer? Nesse caso, os rótulos abaixo se movem conforme o esperado? Verifique se os dois foram verificados antes de redimensionar para ver como suas restrições moverão suas visualizações:

Menu IB

Se a vista estiver bloqueada, siga as vistas abaixo e verifique se uma delas não possui um espaço superior para a restrição de visão geral. Em seguida, verifique se a opção de número de linhas para o rótulo está definida como 0 e ele deve cuidar do resto.

Cory Imdieke
fonte
Sim, depois de muita brincadeira, consegui o efeito que queria. Você só precisa pensar com muito cuidado sobre as restrições que deseja. Não ajuda que o IB esteja constantemente criando restrições que você não espera.
precisa
5

Acho que você precisa do seguinte:

  • Uma restrição principal
  • Uma restrição principal (por exemplo, lado esquerdo)
  • Uma restrição à direita (por exemplo, lado direito)
  • Defina o conteúdo que prioriza prioridade, horizontal para baixo, para preencher o espaço especificado se o texto for curto.
  • Defina a resistência à compactação de conteúdo, na horizontal, para baixa, para que ela se enrole em vez de tentar se tornar maior.
  • Defina o número de linhas como 0.
  • Defina o modo de quebra de linha como quebra de linha.
Chris
fonte
Isso deve funcionar no xcode9. Não precisei fazer nada com a restrição de altura da etiqueta (não tenho nenhuma): só funciona fora da caixa ultimamente depois que a etiqueta fica restrita (à direita, à esquerda, à esquerda e à direita). uikit faz o resto
Anton Tropashko 25/17
O conteúdo que abraçava a prioridade era o meu problema. Obrigado por me informar sobre essa propriedade, bem como sobre a resistência à compressão. Você me ajudou a resolver um problema de horas.
David Gay
0

Nenhuma das diferentes soluções encontradas nos diversos tópicos sobre o assunto funcionou perfeitamente para o meu caso (x rótulos de múltiplas linhas dinâmicas nas células de exibição de tabela dinâmica).

Eu encontrei uma maneira de fazê-lo:

Depois de definir as restrições em seu rótulo e definir sua propriedade multilinha para 0, faça uma subclasse de UILabel; Liguei para o meu AutoLayoutLabel:

@implementation AutoLayoutLabel

- (void)layoutSubviews{
    [self setNeedsUpdateConstraints];
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
}

@end
Tanguy G.
fonte
0

Eu tenho um UITableViewCell que tem um rótulo de quebra de texto. Eu trabalhei com quebra de texto da seguinte forma.

1) Defina as restrições do UILabel da seguinte maneira.

insira a descrição da imagem aqui

2) Defina no. de linhas para 0.

3) Adicionada restrição de altura do UILabel ao UITableViewCell.

@IBOutlet weak var priorityLabelWidth: NSLayoutConstraint!

4) No UITableViewCell:

priorityLabel.sizeToFit()
priorityLabelWidth.constant = priorityLabel.intrinsicContentSize().width+5
AG
fonte
-12

Uma maneira de fazer isso ... À medida que o comprimento do texto aumenta, tente alterar (diminuir) o tamanho da fonte do texto do rótulo usando

Label.adjustsFontSizeToFitWidth = YES;
user1662392
fonte
1
Quero que as diferentes instâncias do controlador de exibição tenham uma aparência consistente, portanto, não quero alterar o tamanho da fonte.
James Harpe