Выровнять текст по вертикали внутри UILabel - PullRequest
2098 голосов
/ 28 июня 2009

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

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

image representing a UILabel with vertically-centered text

Ответы [ 48 ]

2645 голосов
/ 28 июня 2009

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

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

    [myLabel sizeToFit];

sizeToFit to squeeze a label


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

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Longer label text with sizeToFit


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

Я сделаю свою метку в коде, чтобы вы могли видеть, что происходит. Вы можете настроить большую часть этого в Интерфейсном Разработчике также. Моя настройка - приложение на основе вида с фоновым изображением, которое я сделал в 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 в комментариях:

Если ваша метка включена в перо или раскадровку в качестве подпредставления view ViewController, который использует autolayout, то размещение вашего вызова sizeToFit в viewDidLoad не будет работать, потому что размеры autoolayout и расположение subviews после вызова 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;
329 голосов
/ 27 августа 2010
  1. Установить новый текст:

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

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

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

    [myLabel sizeToFit]
    

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

Кроме того, на моем ярлыке включена функция переноса слов.

157 голосов
/ 09 сентября 2010

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

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.

138 голосов
/ 05 октября 2011

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

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

enter image description here

99 голосов
/ 21 мая 2012

Без суеты, без суеты

@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

Без суеты, без 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)
    }

}

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

}
77 голосов
/ 04 марта 2010

Как и в ответе выше, но это было не совсем правильно или просто вставлять в код, поэтому я немного его исправил. Добавьте это расширение либо в собственный файл .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];
68 голосов
/ 19 апреля 2010

Еще более быстрый (и более грязный) способ сделать это - установить для режима прерывания строки UILabel значение «Клип» и добавить фиксированное количество новых строк.

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

Это решение не будет работать для всех - в частности, если вы все еще хотите показывать "..." в конце вашей строки, если оно превышает количество отображаемых строк, вам нужно используйте один из более длинных фрагментов кода - но во многих случаях вы получите то, что вам нужно.

61 голосов
/ 10 февраля 2012

Вместо UILabel вы можете использовать UITextField, который имеет опцию вертикального выравнивания:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
49 голосов
/ 16 октября 2012

Я долго боролся с этим и хотел поделиться своим решением.

Это даст вам UILabel, который будет автоматически сжимать текст до 0,5 масштаба и вертикально центрировать текст. Эти опции также доступны в раскадровке / IB.

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
43 голосов
/ 04 августа 2011

Создать новый класс

LabelTopAlign

.h файл

#import <UIKit/UIKit.h>


@interface KwLabelTopAlign : UILabel {

}

@end

.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

Редактировать

Вот более простая реализация, которая делает то же самое:

#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
...