Alinhar verticalmente o texto para cima em um UILabel

2175

eu tenho um UILabel espaço para duas linhas de texto. Às vezes, quando o texto é muito curto, esse texto é exibido no centro vertical do rótulo.

Como alinhar verticalmente o texto para estar sempre na parte superior do UILabel?

imagem representando um UILabel com texto centralizado verticalmente

Stefan
fonte

Respostas:

2702

Não há como definir o alinhamento vertical em um UILabel , mas você pode obter o mesmo efeito alterando o quadro do rótulo. Eu deixei minhas etiquetas em laranja para que você possa ver claramente o que está acontecendo.

Aqui está a maneira rápida e fácil de fazer isso:

    [myLabel sizeToFit];

sizeToFit para comprimir um rótulo


Se você tiver um rótulo com texto mais longo que formará mais de uma linha, defina numberOfLinescomo 0(zero aqui significa um número ilimitado de linhas).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Texto de etiqueta mais longo com sizeToFit


Versão Longa

Farei meu rótulo no código para que você possa ver o que está acontecendo. Você também pode configurar a maior parte disso no Interface Builder. Minha configuração é um aplicativo baseado em exibição com uma imagem de fundo que fiz no Photoshop para mostrar margens (20 pontos). A etiqueta é uma cor laranja atraente, para que você possa ver o que está acontecendo com as dimensões.

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 20 point top and left margin. Sized to leave 20 pt at right.
    CGRect labelFrame = CGRectMake(20, 20, 280, 150);
    UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
    [myLabel setBackgroundColor:[UIColor orangeColor]];

    NSString *labelText = @"I am the very model of a modern Major-General, I've information vegetable, animal, and mineral";
    [myLabel setText:labelText];

    // Tell the label to use an unlimited number of lines
    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

    [self.view addSubview:myLabel];
}

Algumas limitações do uso sizeToFitentram em jogo com o texto alinhado ao centro ou à direita. Aqui está o que acontece:

    // myLabel.textAlignment = NSTextAlignmentRight;
    myLabel.textAlignment = NSTextAlignmentCenter;

    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

insira a descrição da imagem aqui

O rótulo ainda é dimensionado com um canto superior esquerdo fixo. Você pode salvar a largura da etiqueta original em uma variável e defini-la depois sizeToFitou fornecer uma largura fixa para combater esses problemas:

    myLabel.textAlignment = NSTextAlignmentCenter;

    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

    CGRect myFrame = myLabel.frame;
    // Resize the frame's width to 280 (320 - margins)
    // width could also be myOriginalLabelFrame.size.width
    myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
    myLabel.frame = myFrame;

alinhamento de etiquetas


Observe que sizeToFitrespeitará a largura mínima da sua etiqueta inicial. Se você começar com um rótulo com 100 de largura e chamar sizeToFit, ele retornará um rótulo (possivelmente muito alto) com 100 (ou um pouco menos) de largura. Convém definir seu rótulo para a largura mínima desejada antes de redimensionar.

Corrija o alinhamento da etiqueta redimensionando a largura do quadro

Algumas outras coisas a serem observadas:

Se lineBreakModeé respeitado depende de como está definido. NSLineBreakByTruncatingTail(o padrão) é ignorado depois sizeToFit, assim como os outros dois modos de truncamento (cabeça e meio). NSLineBreakByClippingtambém é ignorado. NSLineBreakByCharWrappingfunciona como de costume. A largura do quadro ainda é reduzida para caber na letra mais à direita.


Mark Amery deu uma correção para NIBs e Storyboards usando o Auto Layout nos comentários:

Se o seu marcador estiver incluído em uma ponta ou storyboard como uma viewsub-visualização de um ViewController que usa o layout automático, a sizeToFitchamada viewDidLoadserá feita não funcionará, porque o layout automático dimensiona e posiciona as sub-visualizações após a viewDidLoadchamada e desfaz imediatamente os efeitos de seu sizeToFitligar. No entanto, chamar sizeToFitde dentro viewDidLayoutSubviews vontade trabalho.


Minha resposta original (para posteridade / referência):

Isso usa o NSStringmétodo sizeWithFont:constrainedToSize:lineBreakMode:para calcular a altura do quadro necessária para ajustar uma sequência e, em seguida, define a origem e a largura.

Redimensione o quadro da etiqueta usando o texto que você deseja inserir. Dessa forma, você pode acomodar qualquer número de linhas.

CGSize maximumSize = CGSizeMake(300, 9999);
NSString *dateString = @"The date today is January 1st, 1999";
UIFont *dateFont = [UIFont fontWithName:@"Helvetica" size:14];
CGSize dateStringSize = [dateString sizeWithFont:dateFont 
        constrainedToSize:maximumSize 
        lineBreakMode:self.dateLabel.lineBreakMode];

CGRect dateFrame = CGRectMake(10, 10, 300, dateStringSize.height);

self.dateLabel.frame = dateFrame;
rei nevan
fonte
10
você precisa remover o autolayout
hungbm06 22/10
4
Aqui está uma maneira simples de resolver o problema: stackoverflow.com/a/26632490/636559
AboulEinein
Se você usar o layout automático e o storyboard, adicionar uma restrição com a opção "Remover no momento da construção" marcada ajudará
JK ABC
Se você precisa este trabalho com adjustsFontSizeToFitWidth, em seguida, usar SizeToFit em seguida, definir o quadro do rótulo para 0, 0, theWidthYouWant, label.frame.size.height (que é a nova altura determinada pela SizeToFit) em seguida, aplicaradjustsFontSizeToFitWidth
Albert Renshaw
3
Esta resposta é desnecessariamente complicada e está usando uma "palavra ao redor". Consulte a sugestão abaixo de simplesmente alterar o conteúdo vertical que abraça a prioridade. Não requer nenhum código ...
beetree 23/01
335
  1. Defina o novo texto:

    myLabel.text = @"Some Text"
  2. Defina as maximum numberlinhas como 0 (automático):

    myLabel.numberOfLines = 0
  3. Defina o quadro do rótulo para o tamanho máximo:

    myLabel.frame = CGRectMake(20,20,200,800)
  4. Ligue sizeToFitpara reduzir o tamanho do quadro e ajustar o conteúdo:

    [myLabel sizeToFit]

O quadro de etiquetas agora é alto e largo o suficiente para caber no seu texto. O canto superior esquerdo deve permanecer inalterado. Eu testei isso apenas com o texto alinhado à esquerda superior. Para outros alinhamentos, pode ser necessário modificar o quadro posteriormente.

Além disso, meu rótulo tem quebra de linha ativada.

Jakob Egger
fonte
Ei Ben, acabei de olhar meu código antigo novamente e atualizei minha resposta com as etapas corretas necessárias.
Jakob Egger
Isso funcionou bem pra mim. Defino as dimensões do meu UILabel e alterei numberOfLines para 0 no IB e depois de definir o texto chamado [myLabel sizeToFit].
Dan Ellis
funciona perfeitamente para mim, depois de definir o número de linhas no Attr. Inspector
d2burke
Não funciona em caso de número de linhas superior a 1
Tom
Não funciona quando você deseja um rótulo de duas linhas, mas o texto requer mais linhas.
Jose Manuel Abarca Rodríguez
159

Referindo-se à solução de extensão:

for(int i=1; i< newLinesToPad; i++) 
    self.text = [self.text stringByAppendingString:@"\n"];

deve ser substituído por

for(int i=0; i<newLinesToPad; i++)
    self.text = [self.text stringByAppendingString:@"\n "];

É necessário espaço adicional em cada nova linha adicionada, porque UILabelsos retornos de carro à direita do iPhone parecem ser ignorados :(

Da mesma forma, alignBottom também deve ser atualizado com um @" \n@%"no lugar de "\n@%"(para a inicialização do ciclo deve ser substituída por "for (int i = 0 ..." também).

A seguinte extensão funciona para mim:

// -- file: UILabel+VerticalAlign.h
#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end

// -- file: UILabel+VerticalAlign.m
@implementation UILabel (VerticalAlign)
- (void)alignTop {
    CGSize fontSize = [self.text sizeWithFont:self.font];
    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label
    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;
    for(int i=0; i<newLinesToPad; i++)
        self.text = [self.text stringByAppendingString:@"\n "];
}

- (void)alignBottom {
    CGSize fontSize = [self.text sizeWithFont:self.font];
    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label
    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;
    for(int i=0; i<newLinesToPad; i++)
        self.text = [NSString stringWithFormat:@" \n%@",self.text];
}
@end

Em seguida, ligue [yourLabel alignTop];ou [yourLabel alignBottom];após cada atribuição de texto yourLabel.

Dmitri Sologoubenko
fonte
@DS Notei que você está adicionando VerticalAlignentre parênteses depois @implementation UILabel. Sendo novo no Objective-C, nunca encontrei essa sintaxe antes. Como isso é chamado?
Julian
Incrível, não sabia sobre categorias, isso me dá ainda mais apreço pela linguagem Objective-c. Aprender mais idiomas é tão importante para se tornar um bom programador.
JeroenEijkhof
Deu um voto positivo. mas iirc isso não funcionará se você não souber o número de linhas que é um ponto negativo
Tjirp
Eu tenho que mostrar 3 linhas de texto e alinhar o texto para cima. Então, eu tenho que definir o número de linhas como 3. Quando o defino como 3, estou vendo "..." se o texto for menor (que se encaixa em uma linha). Como evitar esse "..."
Satyam
1
Publiquei uma edição desta categoria, espero que seja aprovada em breve. Os métodos sizeWithFont:constrainedToSize:lineBreakMode:e sizeWithFont: são depreciados no iOS7. Além disso, esta categoria só funciona nos rótulos quando numberOfLines é maior que 0.
timgcarlson
146

Caso isso ajude alguém, tive o mesmo problema, mas consegui resolver o problema simplesmente mudando de usar UILabelpara usar UITextView. Compreendo que isso não seja para todos, porque a funcionalidade é um pouco diferente.

Se você mudar para o uso UITextView, poderá desativar todas as propriedades da Visualização de rolagem, bem como a Interação do usuário ativada ... Isso o forçará a agir mais como um rótulo.

insira a descrição da imagem aqui

jowie
fonte
1
Não tenho certeza de como isso afetará o desempenho se muitos UILabels forem convertidos em visualizações de texto.
Ninjaneer
2
Todas as outras soluções neste segmento não funcionaram para mim no iOS 7 (por qualquer motivo). Mas o simples uso de um UITextViewme deu o resultado desejado imediatamente.
Clifton Labrum
1
É uma solução alternativa, ele ainda precisa de alguns ajustes para torná-la autêntica, mas, de fato, esta é a solução mais fácil de código nenhum que eu vi.
Itai Spector
1
Eu gosto desta solução. Concedido, pode haver problemas de desempenho e tal, para um simples rótulo em uma visão relativamente simples, este foi perfeito para o que eu precisava (e não consigo perceber qualquer impacto de desempenho)
JCutting8
1
Uma pequena desvantagem desse método é que ele introduzirá um preenchimento intrínseco extra no texto. Uma solução rápida é introduzir uma restrição negativa.
Sira Lam
122

Sem confusão, sem confusão

@interface MFTopAlignedLabel : UILabel

@end


@implementation MFTopAlignedLabel

- (void)drawTextInRect:(CGRect) rect
{
    NSAttributedString *attributedText = [[NSAttributedString alloc]     initWithString:self.text attributes:@{NSFontAttributeName:self.font}];
    rect.size.height = [attributedText boundingRectWithSize:rect.size
                                            options:NSStringDrawingUsesLineFragmentOrigin
                                            context:nil].size.height;
    if (self.numberOfLines != 0) {
        rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight);
    }
    [super drawTextInRect:rect];
}

@end

Sem confusão, sem Objective-c, sem confusão, mas Swift 3:

class VerticalTopAlignLabel: UILabel {

    override func drawText(in rect:CGRect) {
        guard let labelText = text else {  return super.drawText(in: rect) }

        let attributedText = NSAttributedString(string: labelText, attributes: [NSFontAttributeName: font])
        var newRect = rect
        newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

        if numberOfLines != 0 {
            newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
        }

        super.drawText(in: newRect)
    }

}

Swift 4.2

class VerticalTopAlignLabel: UILabel {

    override func drawText(in rect:CGRect) {
        guard let labelText = text else {  return super.drawText(in: rect) }

        let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
        var newRect = rect
        newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

        if numberOfLines != 0 {
            newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
        }

        super.drawText(in: newRect)
    }

}
jasongregori
fonte
A versão rápida funcionou para mim sem efeitos colaterais! Ótimo trabalho!
l.vasilev
2
Provavelmente a melhor resposta aqui, funciona em todos os cenários. sizeToFit não funciona se o rótulo estiver em UITableviewCell. Bom trabalho.
Chauhan rajat
79

Como a resposta acima, mas não era bem correta ou fácil de inserir código, então eu limpei um pouco. Adicione esta extensão ao seu próprio arquivo. He. Ou simplesmente cole logo acima da implementação que você pretende usar:

#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end


@implementation UILabel (VerticalAlign)
- (void)alignTop
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

    for(int i=0; i<= newLinesToPad; i++)
    {
        self.text = [self.text stringByAppendingString:@" \n"];
    }
}

- (void)alignBottom
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

    for(int i=0; i< newLinesToPad; i++)
    {
        self.text = [NSString stringWithFormat:@" \n%@",self.text];
    }
}
@end

E, para usar, coloque seu texto no rótulo e chame o método apropriado para alinhá-lo:

[myLabel alignTop];

ou

[myLabel alignBottom];
BadPirate
fonte
sizeWithFont obsoleto
tdios 18/12/19
68

Uma maneira ainda mais rápida (e mais suja) de conseguir isso é definir o modo de quebra de linha do UILabel como "Clip" e adicionar uma quantidade fixa de novas linhas.

myLabel.lineBreakMode = UILineBreakModeClip;
myLabel.text = [displayString stringByAppendingString:"\n\n\n\n"];

Esta solução não funcionará para todos - em particular, se você ainda deseja mostrar "..." no final da sua string, se exceder o número de linhas que você está mostrando, será necessário usar um dos os bits de código mais longos - mas, em muitos casos, você terá o que precisa.

Purple Ninja Girl
fonte
3
Definir o modo de quebra de linha como 'clipe' parece atrapalhar o dimensionamento automático da etiqueta. Use em UILineBreakModeWordWrapvez disso.
Niels van der Rest
e melhor adicionar espaços "\ n \ n"
djdance 15/01
68

Abordagem mais fácil usando o Storyboard:

Incorporar etiqueta no StackView e defina os seguintes dois atributos do StackView no Attribute Inspector:

1- AxisaHorizontal ,

2- AlignmentaTop

insira a descrição da imagem aqui

Baig
fonte
1
Para obter melhores resultados, você pode fazer isso StackView> View> UILabel, porque quando você apenas incorpora um UILabel dentro de um StackView, o StackView é redimensionado para ajustar o UILabel ao tamanho mínimo
Daniel Beltrami
Ótima idéia para colocar UILabelem alguma UIViewsubclasse ( UIViewnormalmente é o suficiente) e deixar esse rótulo crescer na direção descendente. Obrigado!
Viktor Kucera
1
Impressionante! Também vale a pena mencionar que você precisa restrições claras sobre o UILabele deixe a StackView fazer o seu trabalho. Obrigado!
Luiz Dias
Com o comando de menu Editor-> Incorporar com o rótulo selecionado, as restrições de rótulos são limpas para você pelo Xcode, você pode então apenas definir as restrições para o StackView. Essa abordagem é a mais próxima de uma abordagem de layout 'SwiftUI', que foi como eu a descobri.
Rocket Garden
por que isso funciona?
Novellizator
60

Em vez de UILabelvocê pode usar a UITextFieldopção de alinhamento vertical:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
ivanzoid
fonte
7
Advertência: O UITextField permite apenas uma única linha de texto.
David James
2
Para concluir o comentário do @DavidJames: UITextView seria mais apropriado #
CedricSoubrie
49

Eu luto com esse problema há muito tempo e queria compartilhar minha solução.

Isso fornecerá um UILabeltexto que reduzirá automaticamente o texto a 0,5 escalas e centralizará o texto verticalmente. Essas opções também estão disponíveis no Storyboard / IB.

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
David Greco
fonte
Funcionou como perfeito entre todas as respostas fornecidas. Obrigado @David Greco. Juntamente com essas propriedades, se definirmos o ajuste de ajustamentoFontSizeToFitWidth para YES, o texto caberá no quadro sem modificar o quadro do rótulo.
Ashok
43

Crie uma nova classe

LabelTopAlign

arquivo .h

#import <UIKit/UIKit.h>


@interface KwLabelTopAlign : UILabel {

}

@end

arquivo .m

#import "KwLabelTopAlign.h"


@implementation KwLabelTopAlign

- (void)drawTextInRect:(CGRect)rect {
    int lineHeight = [@"IglL" sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, 9999.0f)].height;
    if(rect.size.height >= lineHeight) {
        int textHeight = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, rect.size.height)].height;
        int yMax = textHeight;
        if (self.numberOfLines > 0) {
            yMax = MIN(lineHeight*self.numberOfLines, yMax);    
        }

        [super drawTextInRect:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, yMax)];
    }
}

@end

Editar

Aqui está uma implementação mais simples que faz o mesmo:

#import "KwLabelTopAlign.h"

@implementation KwLabelTopAlign

- (void)drawTextInRect:(CGRect)rect
{
    CGFloat height = [self.text sizeWithFont:self.font
                            constrainedToSize:rect.size
                                lineBreakMode:self.lineBreakMode].height;
    if (self.numberOfLines != 0) {
        height = MIN(height, self.font.lineHeight * self.numberOfLines);
    }
    rect.size.height = MIN(rect.size.height, height);
    [super drawTextInRect:rect];
}

@end
sebastian friedrich
fonte
+1 para a resposta real. Ao contrário das sizeToFitsoluções isso realmente faz a UILabelcolocar qualquer texto na parte superior, mesmo se você substituir dinamicamente um texto mais curto com um mais longo, ou vice-versa.
SnakE
38

insira a descrição da imagem aqui

No Interface Builder

  • Conjunto UILabel para o tamanho do maior texto possível
  • Defina Linescomo '0' no Inspetor de atributos

No seu código

  • Defina o texto do rótulo
  • Ligue sizeToFitpara sua etiqueta

Fragmento de código:

self.myLabel.text = @"Short Title";
[self.myLabel sizeToFit];
bearMountain
fonte
funcionou muito bem, exceto no meu caso, eu precisava definir linhas como 2 - ter um UILabel dentro de um UITableViewCell e definir 0 fez sobrepor, mas definir 2 funcionou (a célula tem espaço suficiente para 2 linhas)
eselk
34

Para a UI adaptativa (iOS8 ou posterior), o Alinhamento vertical do UILabel deve ser definido no StoryBoard, alterando as propriedades noOfLines= 0` e

Restrições

  1. Ajustando as restrições UILabel LefMargin, RightMargin e Top Margin.

  2. Mude Content Compression Resistance Priority For Vertical= 1000` para que Vertical> Horizontal.

Restrições do UILabel

Editado:

noOfLines=0

e as seguintes restrições são suficientes para alcançar os resultados desejados.

insira a descrição da imagem aqui

Rabindra Nath Nandi
fonte
Isso foi demais. Você pode explicar por que a Vertical> Horizontal fez o trabalho? Obrigado.
Gabriel
33

Crie uma subclasse de UILabel. Funciona como um encanto:

// TopLeftLabel.h

#import <Foundation/Foundation.h>

@interface TopLeftLabel : UILabel 
{
}

@end

// TopLeftLabel.m

#import "TopLeftLabel.h"

@implementation TopLeftLabel

- (id)initWithFrame:(CGRect)frame 
{
    return [super initWithFrame:frame];
}

- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines 
{
    CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];    
    textRect.origin.y = bounds.origin.y;
    return textRect;
}

-(void)drawTextInRect:(CGRect)requestedRect 
{
    CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
    [super drawTextInRect:actualRect];
}

@end

Como discutido aqui .

Martin Wickman
fonte
27

Eu escrevi uma função util para atingir esse objetivo. Você pode dar uma olhada:

// ajusta a altura de uma etiqueta com várias linhas para alinhar verticalmente com a parte superior
+ (vazio) alignLabelWithTop: (UILabel *) rótulo {
  CGSize maxSize = CGSizeMake (label.frame.size.width, 999);
  label.adjustsFontSizeToFitWidth = NO;

  // obtém a altura real
  CGSize actualSize = [label.text sizeWithFont: label.font constrainedToSize: maxSize lineBreakMode: label.lineBreakMode];
  CGRect rect = label.frame;
  rect.size.height = actualSize.height;
  label.frame = rect;
}

.Como usar? (Se lblHello for criado pelo construtor de interface, pulo alguns detalhes dos atributos do UILabel)

lblHello.text = @ "Olá Mundo! Olá Mundo! Olá Mundo! Olá Mundo! Olá Mundo! Olá Mundo! Olá Mundo! Olá Mundo! Olá Mundo! Olá Mundo!";
lblHello.numberOfLines = 5;
[Utilitários alignLabelWithTop: lblHello];

Também escrevi no meu blog como um artigo: http://fstoke.me/blog/?p=2819

fogo de artifício
fonte
27

Demorei um pouco para ler o código, bem como o código na página apresentada, e descobri que todos eles tentavam modificar o tamanho do quadro do rótulo, para que o alinhamento vertical central padrão não aparecesse.

No entanto, em alguns casos, queremos que o rótulo ocupe todos esses espaços, mesmo que o rótulo tenha muito texto (por exemplo, várias linhas com a mesma altura).

Aqui, usei uma maneira alternativa de resolvê-lo, simplesmente preenchendo as novas linhas até o final do rótulo (observe que eu realmente herdei o UILabel , mas não é necessário):

CGSize fontSize = [self.text sizeWithFont:self.font];

finalHeight = fontSize.height * self.numberOfLines;
finalWidth = size.width;    //expected width of label

CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];

int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

for(int i = 0; i < newLinesToPad; i++)
{
    self.text = [self.text stringByAppendingString:@"\n "];
}
Walty Yeung
fonte
22

O que fiz no meu aplicativo foi definir a propriedade da linha do UILabel como 0, além de criar uma restrição inferior ao UILabel e garantir que ele esteja sendo definido como> = 0, como mostra a imagem abaixo. insira a descrição da imagem aqui

Jack Tiong
fonte
19

Peguei as sugestões aqui e criei uma exibição que pode envolver um UILabel e dimensioná-lo e definir o número de linhas para que fique alinhado com o topo. Basta colocar um UILabel como uma subview:

@interface TopAlignedLabelContainer : UIView
{
}

@end

@implementation TopAlignedLabelContainer

- (void)layoutSubviews
{
    CGRect bounds = self.bounds;

    for (UILabel *label in [self subviews])
    {
        if ([label isKindOfClass:[UILabel class]])
        {
            CGSize fontSize = [label.text sizeWithFont:label.font];

            CGSize textSize = [label.text sizeWithFont:label.font
                                     constrainedToSize:bounds.size
                                         lineBreakMode:label.lineBreakMode];

            label.numberOfLines = textSize.height / fontSize.height;

            label.frame = CGRectMake(0, 0, textSize.width,
                 fontSize.height * label.numberOfLines);
        }
    }
}

@end

fonte
19

Você pode usar TTTAttributedLabel , ele suporta alinhamento vertical.

@property (nonatomic) TTTAttributedLabel* label;
<...>

//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
Andrew Romanov
fonte
16

Descobri que as respostas para esta pergunta estão um pouco desatualizadas, então adicione isso para os fãs de layout automático.

O layout automático torna esse problema bastante trivial. Supondo que estamos adicionando o rótulo a UIView *view, o código a seguir fará isso:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setText:@"Some text here"];
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addSubview:label];

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[label]|" options:0 metrics:nil views:@{@"label": label}]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]" options:0 metrics:nil views:@{@"label": label}]];

A altura da etiqueta será calculada automaticamente (usando-a intrinsicContentSize) e a etiqueta será posicionada borda a borda horizontalmente, na parte superior da view.

petehare
fonte
15

Eu usei muitos dos métodos acima e só quero adicionar uma abordagem rápida e suja que usei:

myLabel.text = [NSString stringWithFormat:@"%@\n\n\n\n\n\n\n\n\n",@"My label text string"];

Verifique se o número de novas linhas na sequência fará com que qualquer texto preencha o espaço vertical disponível e defina o UILabel para truncar qualquer texto excedente.

Porque às vezes bom o suficiente é bom o suficiente .

Code Roadie
fonte
No entanto, dada a variação nas alturas do tamanho da tela dos dispositivos iOS, seria necessário haver um número variável de '\ n' para explicar a alteração da altura do rótulo do usuário (que é uma possibilidade). Assim, este método não é particularmente útil a longo prazo.
Rambatino 15/09/14
Nota: "rápido e sujo", não "solução perfeita para todos os tempos". Apenas dizendo'.
Code Roadie
2
@CodeRoadie Rápido e sujo fez o truque para mim, eu tive alguns problemas de layout com a resposta real. Eu poderia fazê-lo da "maneira certa", mas obrigado por me poupar algum tempo!
Séraphin Hochart 27/03
@dGambit observe: "rápido e sujo "
Code Roadie
1
Algo que aprendi recentemente ao carregar dinamicamente visualizações UITextViewé que ela pode chamar dlopenpara suportar a entrada de texto. Isso pode causar um grande atraso no thread da interface do usuário, portanto, essa abordagem é muito mais eficiente!
Página
14

Eu queria ter um rótulo capaz de ter várias linhas, um tamanho mínimo de fonte e centralizar horizontal e verticalmente na visualização principal. Adicionei meu rótulo programaticamente à minha exibição:

- (void) customInit {
    // Setup label
    self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    self.label.numberOfLines = 0;
    self.label.lineBreakMode = UILineBreakModeWordWrap;
    self.label.textAlignment = UITextAlignmentCenter;

    // Add the label as a subview
    self.autoresizesSubviews = YES;
    [self addSubview:self.label];
}

E então, quando eu queria mudar o texto do meu rótulo ...

- (void) updateDisplay:(NSString *)text {
    if (![text isEqualToString:self.label.text]) {
        // Calculate the font size to use (save to label's font)
        CGSize textConstrainedSize = CGSizeMake(self.frame.size.width, INT_MAX);
        self.label.font = [UIFont systemFontOfSize:TICKER_FONT_SIZE];
        CGSize textSize = [text sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
        while (textSize.height > self.frame.size.height && self.label.font.pointSize > TICKER_MINIMUM_FONT_SIZE) {
            self.label.font = [UIFont systemFontOfSize:self.label.font.pointSize-1];
            textSize = [ticker.blurb sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
        }
        // In cases where the frame is still too large (when we're exceeding minimum font size),
        // use the views size
        if (textSize.height > self.frame.size.height) {
            textSize = [text sizeWithFont:self.label.font constrainedToSize:self.frame.size];
        }

        // Draw 
        self.label.frame = CGRectMake(0, self.frame.size.height/2 - textSize.height/2, self.frame.size.width, textSize.height);
        self.label.text = text;
    }
    [self setNeedsDisplay];
}

Espero que ajude alguém!

Johnus
fonte
14

O FXLabel (no github) faz isso imediatamente, configurando label.contentModepara UIViewContentModeTop. Este componente não é fabricado por mim, mas é um componente que uso com freqüência e possui muitos recursos e parece funcionar bem.

jjxtra
fonte
11

para quem estiver lendo isso porque o texto no seu rótulo não está centralizado verticalmente, lembre-se de que alguns tipos de fonte não são projetados igualmente. por exemplo, se você criar um rótulo com o tamanho 16 do zapfino, verá que o texto não está perfeitamente centralizado verticalmente.

no entanto, trabalhar com helvética centralizará verticalmente o seu texto.

Nir Pengas
fonte
Muitas fontes personalizadas não estão alinhadas verticalmente ao centro. UILabel ou UITextView estão sempre alinhados verticalmente ao centro. Não há necessidade de pensar no alinhamento vertical na programação do iOS.
Amit Singh
11

Use textRect(forBounds:limitedToNumberOfLines:).

class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        super.drawText(in: textRect)
    }
}
ma11hew28
fonte
Você senhor é um deus!
Diogo Antunes
1
Essa resposta deve ser muito mais positiva, pois é de longe a solução mais agradável e limpa!
Michael Ochs
10

Subclasse UILabel e restrinja o retângulo de desenho, assim:

- (void)drawTextInRect:(CGRect)rect
{
    CGSize sizeThatFits = [self sizeThatFits:rect.size];
    rect.size.height = MIN(rect.size.height, sizeThatFits.height);

    [super drawTextInRect:rect];
}

Tentei a solução envolvendo o preenchimento de nova linha e, em alguns casos, tive um comportamento incorreto. Na minha experiência, é mais fácil restringir o desenho do retângulo do que mexer numberOfLines.

PS Você pode imaginar facilmente dando suporte ao UIViewContentMode desta maneira:

- (void)drawTextInRect:(CGRect)rect
{
    CGSize sizeThatFits = [self sizeThatFits:rect.size];

    if (self.contentMode == UIViewContentModeTop) {
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }
    else if (self.contentMode == UIViewContentModeBottom) {
        rect.origin.y = MAX(0, rect.size.height - sizeThatFits.height);
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }

    [super drawTextInRect:rect];
}
jrc
fonte
10

Se você estiver usando o layout automático, defina o vertical contentHuggingPriority como 1000, no código ou no IB. No IB, pode ser necessário remover uma restrição de altura, definindo sua prioridade como 1 e excluindo-a.

phatmann
fonte
8

Contanto que você não esteja executando nenhuma tarefa complexa, poderá usar em UITextViewvez deUILabels .

Desative a rolagem.

Se você deseja que o texto seja exibido completamente apenas o usuário sizeToFite os sizeThatFits:métodos

thesummersign
fonte
8

Em rápido,

let myLabel : UILabel!

Para ajustar o texto do seu rótulo à tela e ele está no topo

myLabel.sizeToFit()

Para ajustar a fonte da etiqueta à largura da tela ou tamanho específico da largura.

myLabel.adjustsFontSizeToFitWidth = YES

e algum textAlignment para label:

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified

Dara Tith
fonte
Apenas uma mudança de sintaxe da sintaxe antiga de [myLabel sizeToFit]. Obrigado!
velkoon
7

Esta é uma solução antiga, use o autolayout no iOS> = 6

Minha solução:

  1. Dividir linhas sozinho (ignorando as configurações de quebra de etiqueta)
  2. Desenhar linhas sozinho (ignorando o alinhamento das etiquetas)

@interface UITopAlignedLabel : UILabel

@end

@implementation UITopAlignedLabel

#pragma mark Instance methods

- (NSArray*)splitTextToLines:(NSUInteger)maxLines {
    float width = self.frame.size.width;

    NSArray* words = [self.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    NSMutableArray* lines = [NSMutableArray array];

    NSMutableString* buffer = [NSMutableString string];    
    NSMutableString* currentLine = [NSMutableString string];

    for (NSString* word in words) {
        if ([buffer length] > 0) {
            [buffer appendString:@" "];
        }

        [buffer appendString:word];

        if (maxLines > 0 && [lines count] == maxLines - 1) {
            [currentLine setString:buffer];
            continue;
        }

        float bufferWidth = [buffer sizeWithFont:self.font].width;

        if (bufferWidth < width) {
            [currentLine setString:buffer];
        }
        else {
            [lines addObject:[NSString stringWithString:currentLine]];

            [buffer setString:word];
            [currentLine setString:buffer];
        }
    }

    if ([currentLine length] > 0) {
        [lines addObject:[NSString stringWithString:currentLine]];
    }

    return lines;
}

- (void)drawRect:(CGRect)rect {
    if ([self.text length] == 0) {
        return;
    }

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, self.textColor.CGColor);
    CGContextSetShadowWithColor(context, self.shadowOffset, 0.0f, self.shadowColor.CGColor);

    NSArray* lines = [self splitTextToLines:self.numberOfLines];
    NSUInteger numLines = [lines count];

    CGSize size = self.frame.size;
    CGPoint origin = CGPointMake(0.0f, 0.0f);

    for (NSUInteger i = 0; i < numLines; i++) {
        NSString* line = [lines objectAtIndex:i];

        if (i == numLines - 1) {
            [line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeTailTruncation];            
        }
        else {
            [line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeClip];
        }

        origin.y += self.font.lineHeight;

        if (origin.y >= size.height) {
            return;
        }
    }
}

@end
Sulthan
fonte