it-swarm.com.ru

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

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

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

image representing a UILabel with vertically-centered text

2057
Stefan

Нет способа установить вертикальное выравнивание для 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, как и два других режима усечения (head и middle). NSLineBreakByClipping также игнорируется. NSLineBreakByCharWrapping работает как обычно. Ширина рамки по-прежнему сужается, чтобы соответствовать самой правой букве.


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

Если ваша метка включена в перо или раскадровку как подпредставление view ViewController, который использует autolayout, то помещение вашего вызова sizeToFit в viewDidLoad не будет работать, потому что размеры autoolayout и позиционирование подпредставлений после viewDidLoad вызывают и немедленно отменяются последствия вашего вызова sizeToFit. Однако вызов sizeToFit внутри viewDidLayoutSubviewswill работает.


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

При этом используется метод NSStringsizeWithFont: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;
2617
nevan king
  1. Установите новый текст:

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

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

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

    [myLabel sizeToFit]
    

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

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

322
Jakob Egger

Обращаясь к решению расширения:

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 также должен быть обновлен с помощью @" \[email protected]%" вместо "\[email protected]%" (для инициализации цикла нужно заменить на «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.

155
Dmitri Sologoubenko

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

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

 enter image description here

131
jowie

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

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

}
89
jasongregori

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

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

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

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

67
Purple Ninja Girl

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

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

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

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

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
48
David Greco

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

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
42
sebastian friedrich

enter image description here

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

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

В вашем коде

  • Установить текст метки
  • Позвоните sizeToFit на вашем ярлыке

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

self.myLabel.text = @"Short Title";
[self.myLabel sizeToFit];
38
bearMountain

Решение раскадровки: Самый простой и легкий способ - встроить Label в StackView и установить для Axis в StackView значение Horizontal, для Alignment в Top в Attribute Inspector из Storyboard.

 enter image description here

36
Baig

Создайте подкласс 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

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

33
Martin Wickman

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

Ограничения

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

  2. Измените Content Compression Resistance Priority For Vertical = 1000` так, чтобы Vertical> Horizontal.

 Constraints of UILabel

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

noOfLines=0

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

 enter image description here

32
Rabindra Nath Nandi

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

 // отрегулируйте высоту многострочной метки, чтобы она выровнялась по вертикали с верхом 
 + (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! Hello World!"; 
 lblHello.numberOfLines = 5; 
 [Использует alignLabelWithTop : lblHello]; 

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

27
firestoke

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

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

Здесь я использовал альтернативный способ ее решения, просто добавив новые строки до конца метки (пожалуйста, обратите внимание, что я на самом деле унаследовал 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 "];
}
26
Walty Yeung

Я взял предложения здесь и создал представление, которое может обернуть 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
user486646

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

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

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

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

Авто макет делает этот вопрос довольно тривиальным. Предполагая, что мы добавляем метку к 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.

14
petehare

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

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

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

14
Johnus

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

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

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

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

14
Code Roadie

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

13
jjxtra

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

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

11
Nir Pengas

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

10
phatmann

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

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

    [super drawTextInRect:rect];
}

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

Постскриптум Вы можете легко представить поддержку 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];
}
10
jrc

В Свифте

let myLabel : UILabel!

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

myLabel.sizeToFit()

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

myLabel.adjustsFontSizeToFitWidth = YES

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

myLabel.textAlignment = .center 

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified

7
Dara Tith

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

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

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

7
thesummersign

Используйте textRect(forBounds:limitedToNumberOfLines:).

class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        super.drawText(in: textRect)
    }
}
7
ma11hew28

Это старое решение, используйте автоматическое расположение на 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
6
Sulthan

В UILabel вертикальное выравнивание текста невозможно. Но вы можете динамически изменять высоту метки, используя метод sizeWithFont: для NSString, и просто устанавливайте ее x и y по своему усмотрению.

Вы можете использовать UITextField. Он поддерживает contentVerticalAlignment peoperty , поскольку это подкласс UIControl. Вы должны установить для userInteractionEnabled значение NO, чтобы пользователь не мог набирать на нем текст.

6
atul awasthi

Если вы можете создать свой собственный вид, вы можете сделать что-то вроде этого:

- (void)drawRect:(CGRect)rect
{
    CGRect bounds = self.bounds;
    [self.textColor set];
    [self.text drawInRect:bounds
                 withFont:self.font
            lineBreakMode:UILineBreakModeTailTruncation
                alignment:self.textAlignment];
}
6
jcjimenez

Я знаю, что это старый пост, но выравнивание текста по вертикали - ОГРОМНАЯ проблема (по крайней мере, для меня), и я решил, что должен поделиться этим решением, так как сам не смог его найти. 

Использование drawRect немного дорого, на мой взгляд. Правильный способ заставить UILabel выравниваться по вертикали - не использовать UILabel. Используйте UITextView (многострочный UITextField) и наблюдайте за свойством содержимого следующим образом:

- (UITextView*)textView{
     if(!_textView){
        UIEdgeInsets insets = UIEdgeInsetsMake(0, 50, 0, 5);
        CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
        _textView = [[UITextView alloc]initWithFrame:UIEdgeInsetsInsetRect(frame, insets)];
        _textView.delegate = self;
        _textView.scrollEnabled = YES;
        _textView.bounces = YES;
        _textView.backgroundColor = [UIColor whiteColor];
        [_textView setUserInteractionEnabled:NO];
        [_textView addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
    }
    return _textView;
}

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:    (NSDictionary *)change context:(void *)context {
UITextView *tv = object;

CGFloat height = [tv bounds].size.height;
CGFloat contentheight;

#ifdef __IPHONE_7_0
    contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;
#else
    contentheight = [tv contentSize].height;
#endif

    switch(self.verticalAlignment) {
        case VerticalAlignmentBottom:{
            CGFloat topCorrect = ([tv bounds].size.height - contentheight);
            topCorrect = (topCorrect <0.0 ? 0.0 : topCorrect);
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
        break;
        case VerticalAlignmentMiddle:{
            CGFloat topCorrect = (height - contentheight * [tv zoomScale])/2.0;
            topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
            break;
        case VerticalAlignmentTop:{
            tv.contentOffset = (CGPoint){.x = 0, .y = 0 };
        }
            break;
        default:
            break;
    }
}

В основном, что здесь происходит, мы устанавливаем класс, в котором мы находимся как наблюдатель, смотря на свойство contentSize с параметром NSKeyValueObservingOptionNew, чтобы каждый раз, когда изменяется содержимое, вызывался -(void)observeValueForKeyPath:ofObject:change:context:, а затем вы можете вычислить размер смещения для выравнивания текста. соответственно. 

Я не могу взять кредит на себя, оригинальная идея пришла от здесь . Но это решение не работает на iOS7. После нескольких часов троллинга вокруг SO я нашел следующее: Исправление вертикального выравнивания iOS 7 . Ключевая строка там contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;. По какой-то причине в iOS 7 получение высоты contentSize не работает, но это исправляет. Ни одно из двух решений не сработало само по себе, но после небольшого изменения я смог объединить вышеприведенное решение. 

6
barndog

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

5
Jack
yourLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
5
mbo42

Swift 2.0:

Сделать постоянные значения перечисления в пустом файле Swift.

//  AppRef.Swift

import UIKit
import Foundation

enum UILabelTextPositions : String {

 case VERTICAL_ALIGNMENT_TOP = "VerticalAlignmentTop"
 case VERTICAL_ALIGNMENT_MIDDLE = "VerticalAlignmentMiddle"
 case VERTICAL_ALIGNMENT_BOTTOM = "VerticalAlignmentBottom"

}

Использование расширения UILabel:

Создайте пустой класс Swift и назовите его. Добавьте следующее

//  AppExtensions.Swift

import Foundation
import UIKit

extension UILabel{ 
 func makeLabelTextPosition (sampleLabel :UILabel?, positionIdentifier : String) -> UILabel
 {
  let rect = sampleLabel!.textRectForBounds(bounds, limitedToNumberOfLines: 0)

  switch positionIdentifier
  {
  case "VerticalAlignmentTop":
   sampleLabel!.frame = CGRectMake(bounds.Origin.x+5, bounds.Origin.y, rect.size.width, rect.size.height)
   break;

  case "VerticalAlignmentMiddle":
   sampleLabel!.frame = CGRectMake(bounds.Origin.x+5,bounds.Origin.y + (bounds.size.height - rect.size.height) / 2,
    rect.size.width, rect.size.height);
   break;

  case "VerticalAlignmentBottom":
   sampleLabel!.frame = CGRectMake(bounds.Origin.x+5, bounds.Origin.y + (bounds.size.height - rect.size.height),rect.size.width, rect.size.height);
   break;

  default:
   sampleLabel!.frame = bounds;
   break;
  }
  return sampleLabel!

 }
}

Использование :  

myMessageLabel.makeLabelTextPosition(messageLabel, positionIdentifier: UILabelTextPositions.VERTICAL_ALIGNMENT_TOP.rawValue)
5
A.G

Я также работал над этой конкретной проблемой, поэтому я взял идеи DS и nevan king и в основном объединил их в подкласс, который реализует свойство вертикального выравнивания, которое также позволяет изменять выравнивание более одного раза. Он заимствует тип UIControlContentVerticalAlignment и также поддерживает UIControlContentVerticalAlignmentFill.

Из того, что я видел, numberOfLines кажется бесполезным, когда речь идет о вертикальном выравнивании, поэтому в этом подклассе он всегда равен 0 при применении вертикального выравнивания . Кроме того, вам все равно придется самостоятельно устанавливать lineBreakMode на тот случай, если вы захотите многострочная текстовая метка.

Вот оно: QALabel на GitHub

5
vbwx

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

- (id)init
{
    if (self = [super init]) {
        contentEdgeInsets = UIEdgeInsetsZero;
    }

    return self;
}

- (void)layoutSubviews
{
    CGRect localBounds = self.bounds;
    localBounds = CGRectMake(MAX(0, localBounds.Origin.x + contentEdgeInsets.left), 
                             MAX(0, localBounds.Origin.y + contentEdgeInsets.top), 
                             MIN(localBounds.size.width, localBounds.size.width - (contentEdgeInsets.left + contentEdgeInsets.right)), 
                             MIN(localBounds.size.height, localBounds.size.height - (contentEdgeInsets.top + contentEdgeInsets.bottom)));

    for (UIView *subview in self.subviews) {
        if ([subview isKindOfClass:[UILabel class]]) {
            UILabel *label = (UILabel*)subview;
            CGSize lineSize = [label.text sizeWithFont:label.font];
            CGSize sizeForText = [label.text sizeWithFont:label.font constrainedToSize:localBounds.size lineBreakMode:label.lineBreakMode];

            NSInteger numberOfLines = ceilf(sizeForText.height/lineSize.height);

            label.numberOfLines = numberOfLines;
            label.frame = CGRectMake(MAX(0, contentEdgeInsets.left), MAX(0, contentEdgeInsets.top), localBounds.size.width, MIN(localBounds.size.height, lineSize.height * numberOfLines)); 
        }
    }
}
5
sean woodward

Для Свифта 3 ...

@IBDesignable class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        if let stringText = text {
            let stringTextAsNSString = stringText as NSString
            let labelStringSize = stringTextAsNSString.boundingRect(with: CGSize(width: self.frame.width,height: CGFloat.greatestFiniteMagnitude),
                                                                            options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                                                            attributes: [NSFontAttributeName: font],
                                                                            context: nil).size
            super.drawText(in: CGRect(x:0,y: 0,width: self.frame.width, height:ceil(labelStringSize.height)))
        } else {
            super.drawText(in: rect)
        }
    }
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        layer.borderWidth = 1
        layer.borderColor = UIColor.black.cgColor
    }
}
4
seggy

Есть два способа решить эту проблему. Один из

[mylabel setNumberOfLines:0];
[mylabel sizeToFit];

Но второй способ более надежен для этого подхода, т.е.

 CGSize sizeToFit = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
 [mylabel setFrame:CGRectMake(mylabel.frame.Origin.x, mylabel.frame.Origin.y, sizeToFit.width, sizeToFit.height)];

ввод "\ n" не очень хорошая вещь, но да, если вы знаете ограничения и размер данных, которые будут отображаться, может быть, это работает хорошо, но не может быть расширен, если текст длиннее размера метки. Второй способ в конечном итоге установить рамку в соответствии с размером текста, который будет отображаться.

4
SunnyChattha

Этот код помогает вам выровнять текст по верху, а также сделать высоту надписи для фиксированного содержания текста.

инструкция по использованию кода ниже

isHeightToChange по умолчанию это true, автоматически делает высоту метки равной текстовому содержимому .isHeightToChange, если false, текст выравнивается по верху без уменьшения высоты метки.

#import "DynamicHeightLable.h"

@implementation DynamicHeightLable
@synthesize isHeightToChange;
- (id)initWithFrame:(CGRect)frame 
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // Initialization code.
        self.isHeightToChange=FALSE;//default

    }
    return self;
}
- (void)drawTextInRect:(CGRect)rect
{
    if(!isHeightToChange){
    CGSize maximumLabelSize = CGSizeMake(self.frame.size.width,2500);

    CGFloat height = [self.text sizeWithFont:self.font
                           constrainedToSize:maximumLabelSize
                               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];

}
- (void) layoutSubviews
{
    [super layoutSubviews];
       if(isHeightToChange){
     CGRect ltempFrame = self.frame;
    CGSize maximumLabelSize = CGSizeMake(self.frame.size.width,2500);

    CGFloat height = [self.text sizeWithFont:self.font
                           constrainedToSize:maximumLabelSize
                               lineBreakMode:self.lineBreakMode].height;
    if (self.numberOfLines != 0) {
        height = MIN(height, self.font.lineHeight * self.numberOfLines);
    }
    ltempFrame.size.height = MIN(ltempFrame.size.height, height);

    ltempFrame.size.height=ltempFrame.size.height;
    self.frame=ltempFrame;
       }
}
@end
3
Ramesh Muthe

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

Swift 2.2:

override func layoutSubviews() {
    labelName.sizeToFit()
}

Это решило мою проблему.

3
Elliott Davies

(По состоянию на 7 марта 2018 года)

Swift 4

let maxFrameHeight = 75

myLabel = UILabel()
myLabel.frame = CGRect(x: 9, y: 9, width: 126, height: maxFrameHeight)
myLabel.text = "my labels text displayed"
myLabel.numberOfLines = 0

myLabel.sizeToFit()
let newFrameHeight = myLabel.frame.size.height
let safeNewHeight = min(newFrameHeight, maxFrameHeight)

myLabel.frame = CGRect(x: 9, y: 9, width: 126, height: safeNewHeight)

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

3
BennyTheNerd

Вместо использования sizeToFit вы можете вручную изменить высоту метки;

CGSize size = [descLabel.text sizeWithFont:descLabel.font constrainedToSize:CGSizeMake(labelWidth, labelHeight)];
CGRect frame = descLabel.frame;
frame.size.height = size.height;    
[yourLabel setFrame:frame];

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

3
KarenAnne

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

.h

@interface VAlignLabel : UILabel
@property (nonatomic, assign) WBZVerticalAlignment alignment;
@end


.m

 -(void)setAlignment:(WBZVerticalAlignment)alignment{

    _alignment = alignment;

    CGSize s = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(self.frame.size.width, 9999) lineBreakMode:NSLineBreakByWordWrapping];

    switch (_alignment)
    {
        case wbzLabelAlignmentVerticallyTop:
            self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y, self.frame.size.width, s.height);
            break;
        case wbzLabelAlignmentVerticallyMiddle:
            self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y + (self.frame.size.height - s.height)/2, self.frame.size.width, s.height);
            break;
        case wbzLabelAlignmentVerticallyBottom:
            self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y + (self.frame.size.height - s.height), self.frame.size.width, s.height);
            break;
        default:
            break;
    }
}

-(void)layoutSubviews{
    [self setAlignment:self.alignment];
}
2
cohen72

Простое решение этой проблемы ... 

Поместите UIStackView ниже метки. 

Затем используйте AutoLayout, чтобы установить нулевой вертикальный интервал для метки и ограничить верхнюю часть тем, что было ранее нижней меткой. Метка не будет расти, stackView будет. Что мне нравится в этом, так это то, что представление стека не рендеринг. Так что не стоит тратить время на рендеринг. Даже если это делает расчеты AutoLayout.

Вам, вероятно, нужно будет поиграть с ContentHugging и Resistance, но это также просто. 

Время от времени я гуглю эту проблему и возвращаюсь сюда. На этот раз у меня возникла идея, которую я считаю самой простой, и я не думаю, что это плохая производительность. Я должен сказать, что я просто использую AutoLayout и не хочу беспокоиться о расчете кадров. Это просто ... чёрт.

1
Nuno Gonçalves

Это можно сделать с большей гибкостью, установив height constraint для метки в Интерфейсном Разработчике, привязав его к коду с помощью IBOutlet и изменив эту высоту, чтобы текст отображался в конкретной вертикальной позиции. Пример для выравнивания по центру и дну:

labelHeightConstraint.constant = centerAlignment ? 30 : 15
layoutIfNeeded()
0
Ruben Marin

Вот решение в Swift 4.0:

func viewDidLoad() {

    super.viewDidLoad()

    // 20 point top and left margin. Sized to leave 20 pt at right.
    let labelFrame = CGRect(x: 20, y: 20, width: 280, height: 150)
    let myLabel = UILabel(frame: labelFrame)
    myLabel.backgroundColor = UIColor.orange

    let labelText = "I am the very model of a modern Major-General, 
    I've information vegetable, animal, and mineral"
    myLabel.text = labelText

    // Tell the label to use an unlimited number of lines
    myLabel.numberOfLines = 0
    myLabel.sizeToFit()

    view.addSubview(myLabel) // add label
}
0
Ashish