Вопрос: Вертикально выравнивать текст вверху в UILabel


у меня есть UILabelс пространством для двух строк текста. Иногда, когда текст слишком короткий, этот текст отображается в вертикальном центре метки.

Как вертикально выровнять текст, чтобы он всегда находился в верхней части UILabel?

image representing a UILabel with vertically-centered text


1970


источник


Ответы:


Невозможно установить вертикальное выравнивание на UILabel, но вы можете получить тот же эффект, изменив рамку метки. Я сделал свои этикетки оранжевыми, чтобы вы могли ясно видеть, что происходит.

Вот быстрый и простой способ сделать это:

    [myLabel sizeToFit];

sizeToFit to squeeze a label


Если у вас есть ярлык с более длинным текстом, который будет содержать более одной строки, установите numberOfLinesв 0(ноль здесь означает неограниченное количество строк).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Longer label text with sizeToFit


Более длинная версия

Я сделаю свой ярлык в коде, чтобы вы могли видеть, что происходит. Вы можете настроить большую часть этого в Interface Builder. Моя настройка - приложение на основе просмотра с фоновым изображением, которое я сделал в Photoshop, чтобы показать поля (20 баллов). Этикетка является привлекательным оранжевым цветом, поэтому вы можете видеть, что происходит с размерами.

- (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];
}

Некоторые ограничения использования sizeToFitвступают в игру с выравниванием по центру или по правому краю. Вот что происходит:

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

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

enter image description here

Этикетки по-прежнему имеют фиксированный верхний левый угол. Вы можете сохранить ширину оригинальной метки в переменной и установить ее после sizeToFit, или дать ему фиксированную ширину, чтобы противостоять этим проблемам:

    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;

label alignment


Обратите внимание, что sizeToFitбудет уважать минимальную ширину вашей первоначальной метки. Если вы начинаете с ярлыка 100 в ширину и звоните sizeToFitна нем он вернет вам (возможно, очень высокую) этикетку со 100 (или чуть меньшей) шириной. Возможно, вы захотите установить минимальную ширину метки перед изменением размера.

Correct label alignment by resizing the frame width

Некоторые другие примечания:

Будь то lineBreakModeзависит от того, как он установлен. NSLineBreakByTruncatingTail(по умолчанию) игнорируется после sizeToFit, а также два других режима усечения (голова и середина). NSLineBreakByClippingтакже игнорируется. NSLineBreakByCharWrappingработает как обычно. Ширина рамки все еще сужается, чтобы соответствовать самой правой букве.


Марк Эмери дал исправление для NIB и раскадровки, используя Auto Layout в комментариях:

Если ваш ярлык включен в нить или раскадровку в качестве подсмотра viewViewController, который использует автозапуск, а затем sizeToFitзвонить в viewDidLoadне будет работать, потому что размеры автоопределения и позиции подпунктов после viewDidLoadвызывается и немедленно отменяет действие ваших sizeToFitвызов. Однако вызов sizeToFitизнутри viewDidLayoutSubviews будем Работа.


Мой оригинальный ответ (для потомков / ссылок):

Это использует NSStringметод sizeWithFont:constrainedToSize:lineBreakMode:чтобы вычислить высоту кадра, необходимую для подгонки строки, затем задайте начало и ширину.

Измените размер рамки для метки, используя текст, который вы хотите вставить. Таким образом, вы можете разместить любое количество строк.

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;

2547



  1. Установите новый текст:

    myLabel.text = @"Some Text"
    
  2. Установить maximum numberстрок до 0 (автоматический):

    myLabel.numberOfLines = 0
    
  3. Установите для рамки метки максимальный размер:

    myLabel.frame = CGRectMake(20,20,200,800)
    
  4. Вызов sizeToFitчтобы уменьшить размер рамки, чтобы содержимое просто соответствовало:

    [myLabel sizeToFit]
    

Рамка этикеток теперь просто высока и достаточно широка, чтобы соответствовать вашему тексту. Верхний левый должен быть неизменным. Я тестировал это только с выравниванием по верхнему левому краю текста. Для других выравниваний вам может потребоваться впоследствии изменить кадр.

Кроме того, у моей метки включен перенос слов.


314



Ссылаясь на решение для расширения:

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

следует заменить на

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

В каждой добавленной новой строке требуется дополнительное пространство, потому что iPhone UILabels«Возврат к возврату каретки, кажется, игнорируется :(

Аналогично, alignBottom должен быть обновлен также с помощью @" \n@%"на месте "\n@%"(для инициализации цикла необходимо заменить на «for (int i = 0 ...»).

Для меня работает следующее расширение:

// -- 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

Затем позвоните [yourLabel alignTop];или [yourLabel alignBottom];после каждого назначения текста yourLabel.


151



На всякий случай, это помогло кому-либо, у меня была такая же проблема, но я смог решить проблему, просто переключившись с использования UILabelк использованию UITextView, Я ценю, что это не для всех, потому что функциональность немного отличается.

Если вы переключитесь на использование UITextView, вы можете отключить все свойства «Просмотр прокрутки», а также «Взаимодействие с пользователем» ... Это заставит его действовать как ярлык.

enter image description here


126



Нет мусс, без суеты

@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

Нет muss, нет Objective-c, нет суеты, но 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)
    }

}

78



Как и ответ выше, но это было не совсем правильно, или легко похлопать по коду, поэтому я немного почистил его. Добавьте это расширение либо в собственный файл .h и .m, либо просто вставьте прямо над реализацией, которую вы собираетесь использовать:

#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

И затем, чтобы использовать, поместите свой текст в метку, а затем вызовите соответствующий метод для его выравнивания:

[myLabel alignTop];

или

[myLabel alignBottom];

76



An even quicker (and dirtier) way to accomplish this is by setting the UILabel's line break mode to "Clip" and adding a fixed amount of newlines.

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

This solution won't work for everyone -- in particular, if you still want to show "..." at the end of your string if it exceeds the number of lines you're showing, you'll need to use one of the longer bits of code -- but for a lot of cases this'll get you what you need.


65



Instead of UILabel you may use UITextField which has vertical alignment option:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction

56



I've struggled with this one for a long time and I wanted to share my solution.

This will give you a UILabel that will autoshrink text down to 0.5 scales and vertically center the text. These options are also available in Storyboard/IB.

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];

47



Create a new class

LabelTopAlign

.h file

#import <UIKit/UIKit.h>


@interface KwLabelTopAlign : UILabel {

}

@end

.m file

#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

Edit

Here's a simpler implementation that does the same:

#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

42