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

2175

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

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

изображение, представляющее UILabel с вертикально центрированным текстом

Стефан
источник

Ответы:

2702

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

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

    [myLabel sizeToFit];

sizeToFit, чтобы сжать этикетку


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

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Более длинный текст метки с 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];

введите описание изображения здесь

Ярлык по-прежнему имеет фиксированный верхний левый угол. Вы можете сохранить ширину оригинальной метки в переменной и установить ее после 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;

выравнивание этикетки


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

Правильное выравнивание метки путем изменения ширины рамки

Некоторые другие вещи, чтобы отметить:

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


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

Если ваша метка включена в перо или раскадровку в качестве viewподпредставления ViewController, который использует autolayout, то sizeToFitвызов вашего вызова viewDidLoadне будет работать, потому что размеры autoolayout и положение подпредставлений после вызова 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;
Неван Кинг
источник
10
вам нужно удалить
autolayout
4
Вот простой способ решения проблемы: stackoverflow.com/a/26632490/636559
AboulEinein
Если вы используете автоматический макет и раскадровку, поможет добавление ограничения с опцией «Удалить во время сборки»
JK ABC
Если вам нужно работать с этим, adjustsFontSizeToFitWidthиспользуйте sizeToFit, затем установите для фрейма метки значение 0, 0, theWidthYouWant, label.frame.size.height (это новая высота, определяемая sizeToFit). Затем применитеadjustsFontSizeToFitWidth
Альберт Реншоу,
3
Этот ответ неоправданно сложен и использует слово «вокруг». Пожалуйста, ознакомьтесь с предложением простого изменения приоритета вертикального содержимого. Не требует никакого кода ...
Beetree
335
  1. Установите новый текст:

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

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

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

    [myLabel sizeToFit]

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

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

Якоб Эггер
источник
Привет, Бен, я только что снова посмотрел свой старый код и обновил свой ответ, указав правильные шаги.
Якоб Эггер
Это работало нормально для меня. Я установил размеры моей UILabel и изменил numberOfLines на 0 в IB, а затем после установки текста с именем [myLabel sizeToFit].
Дэн Эллис
у меня отлично работает, после установки количества строк в Аттр. Инспектор
d2burke
Не работает при количестве строк больше 1
Том
Это не работает, когда вы хотите метку из двух строк, но текст требует больше строк.
Жозе Мануэль Абарка Родригес
159

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

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 "];

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

Аналогично, 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.

Дмитрий Сологубенко
источник
@DS Я заметил, что вы добавляете VerticalAlignмежду скобками после @implementation UILabel. Будучи новичком в Objective-C, я не сталкивался с этим синтаксисом раньше. Как это называется?
Джулиан
Удивительно, я не знал о категориях, это дает мне еще большую оценку языка Objective-c. Изучение большего количества языков очень важно, чтобы стать хорошим программистом.
JeroenEijkhof
Дал упор. но iirc это не сработает, если вы не знаете количество строк, что является недостатком
Tjirp
Я должен показать 3 строки текста и выровнять текст сверху. Итак, я должен установить количество строк на 3. Когда я установлю его на 3, я вижу «...», если текст меньше (который помещается в одну строку). Как избежать этого "..."
Satyam
1
Я опубликовал правку этой категории, надеюсь, она будет одобрена в ближайшее время. Методы sizeWithFont:constrainedToSize:lineBreakMode:и sizeWithFont: оба устарели в iOS7. Кроме того, эта категория работает только для меток, когда numberOfLines больше 0.
timgcarlson
146

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

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

введите описание изображения здесь

jowie
источник
1
Не уверен, как это повлияет на производительность, если многие UILabel преобразуются в текстовые представления.
ниндзянир
2
Все остальные решения в этой теме не работали для меня в iOS 7 (по любой причине). Но простое использование сразу UITextViewдало мне желаемый результат.
Клифтон Лабрум
1
Это обходной путь, он все еще нуждается в некоторых корректировках, чтобы он выглядел аутентично, но на самом деле это самое простое решение без кода, которое я когда-либо видел.
Итай Спектор
1
Мне нравится это решение. Конечно, могут быть проблемы с производительностью и тому подобное, для простой метки на относительно простом виде это было идеально для того, что мне нужно (и я не могу заметить какого-либо влияния на производительность)
JCutting8
1
Небольшой недостаток этого метода заключается в том, что он привнесет в текст дополнительные внутренние отступы. Быстрое решение состоит в том, чтобы ввести отрицательное ограничение.
Сира Лам
122

Нет суеты, нет суеты

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

}
jasongregori
источник
Быстрая версия работала для меня без каких-либо побочных эффектов! Отличная работа!
Л.Васильев
2
Вероятно, лучший ответ здесь, работает во всех сценариях. sizeToFit не работает, если метка находится в UITableviewCell. Хорошая работа.
Раджат Чаухан
79

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

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

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

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

Фиолетовая ниндзя
источник
3
Установка режима обрыва строки на «обрезать», кажется, портит автоматическое изменение размера метки. Используйте UILineBreakModeWordWrapвместо этого.
Нильс ван дер Рест
и лучше добавить пробелы "\ n \ n"
djdance
68

Самый простой подход с использованием раскадровки:

Вставить метку в StackView и набор следующих двух атрибутов StackView в Attribute Inspector:

1- AxisдоHorizontal ,

2- AlignmentдоTop

введите описание изображения здесь

Baig
источник
1
Для достижения лучших результатов вы можете сделать это с помощью StackView> View> UILabel, потому что, когда вы просто встраиваете UILabel в StackView, размер StackView изменяется в соответствии с минимальным размером UILabel
Даниэль Бельтрами,
Отличная идея поместить UILabelв некоторый UIViewподкласс ( UIViewобычно достаточно простого ) и позволить этой метке расти в направлении вниз. Спасибо!
Виктор Кучера
1
Потрясающие! Также стоит упомянуть, что вам нужно снять ограничения на UILabelи позволить StackView делать свою работу. Спасибо!
Луис Диас
С помощью команды меню Editor-> Embed с выбранной меткой, ограничения меток очищаются для вас Xcode, затем вы можете просто установить ограничения для StackView. Этот подход наиболее близок к подходу макета SwiftUI, как я его и обнаружил.
Ракетный сад
почему это работает?
Новеллизатор
60

Вместо этого UILabelвы можете использовать UITextFieldвариант с вертикальным выравниванием:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
ivanzoid
источник
7
Предостережение: UITextField допускает только одну строку текста.
Дэвид Джеймс
2
Чтобы завершить комментарий @DavidJames: UITextView будет более уместным
CedricSoubrie
49

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

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

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
Дэвид Греко
источник
Он работал как идеальный среди всех предоставленных ответов. Спасибо, Дэвид Греко. Наряду с этими свойствами, если мы установим AdjustmentsFontSizeToFitWidth на YES, тогда текст будет помещаться в рамку без изменения рамки метки.
Ашок
43

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

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
Себастьян Фридрих
источник
+1 за реальный ответ. В отличие от sizeToFitрешений , это фактически делает UILabelположить любой текст в верхней части, даже если вы динамически заменить более короткий текст с более одной или наоборот.
SnakE
38

введите описание изображения здесь

В Интерфейсном Разработчике

  • Установить UILabelразмер максимально возможного текста
  • Установите Linesв '0' в Инспекторе Атрибутов

В вашем коде

  • Установить текст метки
  • Позвони sizeToFitна свой лейбл

Фрагмент кода:

self.myLabel.text = @"Short Title";
[self.myLabel sizeToFit];
bearMountain
источник
работал отлично, за исключением того, что в моем случае мне нужно было установить строки на 2 - иметь UILabel внутри UITableViewCell и установить значение 0 для перекрытия, но установка на 2 работала (в ячейке достаточно места для 2 строк)
eselk
34

Для Adaptive UI (iOS8 или более поздняя версия) вертикальное выравнивание UILabel должно быть установлено из StoryBoard путем изменения свойств noOfLines= 0` и

Ограничения

  1. Настройка UILabel LefMargin, RightMargin и ограничений верхнего поля.

  2. Изменение Content Compression Resistance Priority For Vertical= 1000` Так что Вертикально> Горизонтально.

Ограничения UILabel

Отредактировано:

noOfLines=0

и следующих ограничений достаточно для достижения желаемых результатов.

введите описание изображения здесь

Рабиндра Натх Нанди
источник
Это было потрясающе. Можете ли вы объяснить, почему вертикальный> горизонтальный сделал работу? Спасибо.
Габриэль
33

Создайте подкласс UILabel. Работает как шарм:

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

Как обсуждено здесь .

Мартин Викман
источник
27

Я написал утилиту для достижения этой цели. Вы можете посмотреть:

// корректируем высоту многострочной метки, чтобы она выровнялась по вертикали с верхом
+ (void) alignLabelWithTop: (UILabel *) label {
  CGSize maxSize = CGSizeMake (label.frame.size.width, 999);
  label.adjustsFontSizeToFitWidth = NO;

  // получаем фактическую высоту
  CGSize actualSize = [label.text sizeWithFont: label.font constrainedToSize: maxSize lineBreakMode: label.lineBreakMode];
  CGRect rect = label.frame;
  rect.size.height = actualSize.height;
  label.frame = rect;
}

.Как пользоваться? (Если lblHello создан Интерфейсным Разработчиком, поэтому я пропущу некоторые детали атрибутов UILabel)

lblHello.text = @ "Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World! Hello World!";
lblHello.numberOfLines = 5;
[Использует alignLabelWithTop: lblHello];

Я также написал это в своем блоге как статью: http://fstoke.me/blog/?p=2819

firestoke
источник
27

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

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

Здесь я использовал альтернативный способ ее решения, просто добавив новые строки до конца метки (пожалуйста, обратите внимание, что я на самом деле унаследовал UILabel, но это не обязательно):

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
источник
22

В моем приложении я установил для свойства линии UILabel значение 0, а также для создания нижнего ограничения UILabel и убедитесь, что для него установлено значение> = 0, как показано на рисунке ниже. введите описание изображения здесь

Джек Тионг
источник
19

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

@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

источник
19

Вы можете использовать TTTAttributedLabel , он поддерживает вертикальное выравнивание.

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

//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
Андрей Романов
источник
16

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

Авто макет делает этот вопрос довольно тривиальным. Предполагая, что мы добавляем метку UIView *view, следующий код выполнит это:

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

Высота метки будет рассчитана автоматически (с использованием ее intrinsicContentSize), и метка будет располагаться от края до края горизонтально вверху view.

petehare
источник
15

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

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

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

Потому что иногда достаточно хорошо, достаточно хорошо .

Код Роуди
источник
Однако, учитывая различия в высоте размера экрана на устройствах iOS, в этом случае потребуется переменное число '\ n', чтобы учесть изменение высоты uilabel (что возможно). Таким образом, этот метод не особенно полезен в долгосрочной перспективе.
Рамбатино
Примечание: «быстро и грязно» не «идеальное решение на все времена». Просто говорю'.
Код Roadie
2
@CodeRoadie Быстрый и грязный сделал свое дело для меня, у меня было несколько проблем с версткой с реальным ответом. Я мог бы сделать это "правильным образом", но спасибо, что сэкономили мне время!
Серафин Хохарт,
@dGambit, пожалуйста, обратите внимание: «быстро и грязно »
Code Roadie
1
Что-то, что я недавно узнал при динамической загрузке представлений, UITextViewэто то, что он может вызывать dlopenдля поддержки ввода текста. Это может вызвать серьезную задержку в потоке пользовательского интерфейса, поэтому этот подход намного эффективнее!
Яно
14

Я хотел иметь метку, которая могла бы иметь несколько строк, минимальный размер шрифта и центрироваться как по горизонтали, так и по вертикали в родительском представлении. Я добавил мой ярлык программно на мой взгляд:

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

А потом, когда я хотел изменить текст моего ярлыка ...

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

Надеюсь, что это помогает кому-то!

Johnus
источник
14

FXLabel (на github) делает это из коробки, установив label.contentModeв UIViewContentModeTop. Этот компонент сделан не мной, но это компонент, который я часто использую, имеет множество функций и, кажется, работает хорошо.

jjxtra
источник
11

для тех, кто читает это, потому что текст внутри вашего ярлыка не центрирован по вертикали, имейте в виду, что некоторые типы шрифтов не разработаны одинаково. например, если вы создадите метку с размером zapfino 16, вы увидите, что текст не идеально отцентрирован по вертикали.

тем не менее, работа с helvetica будет центрировать ваш текст по вертикали.

Нир Пенгас
источник
Многие пользовательские шрифты не выровнены по вертикали. UILabel или UITextView всегда выровнены по центру по вертикали. Не нужно думать о вертикальном выравнивании в программировании iOS.
Амит Сингх
11

Использование 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
источник
Вы, сэр, бог!
Диого Антунес
1
Ответ на этот вопрос должен получить путь больше upvotes , как это, безусловно , прекраснейшим и чистейшим решением!
Майкл Окс
10

Подкласс UILabel и ограничить рисование прямоугольника, как это:

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

    [super drawTextInRect:rect];
}

Я попробовал решение, включающее заполнение новой строки и столкнулся с неправильным поведением в некоторых случаях. По моему опыту, проще ограничить рисование прямоугольника, как указано выше, чем связываться с ним numberOfLines.

PS Вы можете легко представить поддержку UIViewContentMode следующим образом:

- (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
источник
10

Если вы используете autolayout, установите для вертикального contentHuggingPriority значение 1000 в коде или IB. В IB вам, возможно, придется удалить ограничение высоты, установив приоритет 1 и затем удалив его.

phatmann
источник
8

Пока вы не выполняете никаких сложных задач, вы можете использовать UITextViewвместо UILabels.

Отключить прокрутку.

Если вы хотите, чтобы текст отображался полностью, просто пользователь sizeToFitи sizeThatFits:методы

thesummersign
источник
8

Быстро,

let myLabel : UILabel!

Чтобы текст вашего ярлыка соответствовал размеру экрана, он находится сверху

myLabel.sizeToFit()

Чтобы ваш шрифт метки соответствовал ширине экрана или конкретному размеру ширины.

myLabel.adjustsFontSizeToFitWidth = YES

и немного textAlignment для метки:

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified

Дара Тит
источник
Просто изменение синтаксиса от старого синтаксиса [myLabel sizeToFit]. Спасибо!
Velkoon
7

Это старое решение, используйте автоматическое расположение на iOS> = 6

Мое решение:

  1. Разделить линии самостоятельно (игнорируя настройки переноса меток)
  2. Нарисуйте линии самостоятельно (игнорируя выравнивание меток)

@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
источник