Вертикальне вирівнювання тексту вгору в межах UILabel


2175

У мене є UILabelпробіл для двох рядків тексту. Іноді, коли текст занадто короткий, цей текст відображається у вертикальному центрі етикетки.

Як вертикально вирівняти текст, щоб він завжди був у верхній частині UILabel?

зображення, що представляє UILabel з текстом по центру

Відповіді:


2702

Неможливо встановити вертикальне вирівнювання на a UILabel, але ви можете отримати той же ефект, змінивши рамку мітки. Я зробив свої етикетки помаранчевим, щоб ви чітко бачили, що відбувається.

Ось швидкий і простий спосіб зробити це:

    [myLabel sizeToFit];

sizeToFit, щоб видавити етикетку


Якщо у вас є мітка з більш довгим текстом, яка буде містити більше одного рядка, встановіть numberOfLinesзначення 0(нуль тут означає необмежену кількість рядків).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Більш довгий текст мітки з sizeToFit


Більш довга версія

Я зроблю свою мітку в коді, щоб ви могли бачити, що відбувається. Більшу частину цього можна також налаштувати в Interface Builder. Моя настройка - це додаток на основі перегляду з фоновим зображенням, яке я зробив у Photoshop, щоб показати поля (20 балів). Етикетка - привабливий помаранчевий колір, щоб ви могли бачити, що відбувається з розмірами.

- (void)viewDidLoad
{
    [super viewDidLoad];

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

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

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

    [self.view addSubview:myLabel];
}

Деякі обмеження використання sizeToFitграють із текстом, вирівняним у центрі чи правою стороною. Ось що відбувається:

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

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

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

Етикетка все ще розміщена з фіксованим лівим верхнім кутом. Ви можете зберегти ширину оригінальної мітки у змінній та встановити її після 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, як і інші два режими усікання (голова та середина). NSLineBreakByClippingтакож ігнорується. NSLineBreakByCharWrappingпрацює як завжди. Ширина рамки все ще звужується, щоб підходити до правої літери.


Марк Амері в коментарях дав виправлення для НІБ та Дошка розказок, використовуючи функцію автоматичного макета:

Якщо ваша мітка включена в підказку або дошку розкадрувань як підзагляд програми viewViewController, яка використовує автоматичну розкладку, то введення вашого sizeToFitдзвінка viewDidLoadне буде працювати, оскільки виклик автоматичного розкладу та розміщення після viewDidLoadйого перегляду буде негайно скасовано ефекти ваших sizeToFitдзвінок. Тим НЕ менше, виклик sizeToFitзсередини viewDidLayoutSubviews волі роботи.


Мій оригінальний відповідь (для нащадків / довідок):

При цьому використовується NSStringметод sizeWithFont:constrainedToSize:lineBreakMode:обчислення висоти кадру, необхідної для розміщення рядка, а потім встановлюється початок і ширина.

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

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

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

self.dateLabel.frame = dateFrame;

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].
Дан Елліс

прекрасно працює для мене після встановлення кількості рядків в Attr. Інспектор
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@%"замість a "\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];після кожного призначення тексту мітки.


@DS Я помітив, що ви додаєте VerticalAlignміж дужками після @implementation UILabel. Будучи новинкою в Objective-C, я раніше не стикався з цим синтаксисом. Як це називається?
Джуліан

Дивно, що не знав про категорії, це ще більше оцінює мову Objective-c. Вивчити більше мов так важливо, щоб стати хорошим програмістом.
JeroenEijkhof

Дав нагороду. але iirc це не спрацює, якщо ви не знаєте кількість рядків, яка є зворотною стороною
Tjirp

Я повинен показати 3 рядки тексту та вирівняти текст до верху. Отже, чи потрібно мені встановити кількість рядків на 3. Коли я встановив його на 3, я бачу "...", якщо тексту менше (що відповідає одному рядку). Як уникнути цього "..."
Satyam

1
Я опублікував редакцію цієї категорії, сподіваюся, вона скоро буде затверджена. Методи sizeWithFont:constrainedToSize:lineBreakMode:та розмірWithFont: обидва амортизуються в iOS7. Також ця категорія працює лише на етикетках, коли числоOfLines перевищує 0.
timgcarlson

146

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

Якщо ви переключитесь на використання UITextView, ви можете вимкнути всі властивості перегляду прокрутки, а також увімкнено взаємодію користувачів ... Це змусить його діяти більше як ярлик.

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


1
Не впевнені, як це вплине на ефективність, якщо багато UILabels перетворяться на текстові подання.
ninjaneer

2
Усі інші рішення цього потоку не працювали для мене в iOS 7 (з будь-якої причини). Але просто використання UITextViewдало мені бажаний результат одразу.
Кліфтон Лабрум

1
Це вирішення, воно все-таки потребує певних коригувань, щоб воно виглядало автентично, але дійсно це найпростіше рішення без коду, яке я бачив, великі пальці вгору.
Itai Spector

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

}

Швидкий 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)
    }

}

Швидка версія працювала для мене без побічних ефектів! Чудова робота!
l.vasilev

2
Мабуть, найкраща відповідь тут працює у всіх сценаріях. sizeToFit не працює, якщо мітка знаходиться в UITableviewCell. Гарна робота.
rajat chauhan

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

застарілий sizeWithFont
tdios

68

Ще швидший (і брудніший) спосіб досягти цього - встановити режим перерви лінії UILabel на "Кліп" та додавши фіксовану кількість нових рядків.

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

Це рішення працює не для всіх - зокрема, якщо ви все ще хочете показати "..." в кінці рядка, якщо воно перевищує кількість показаних рядків, вам потрібно буде використовувати один із довші біти коду - але для багатьох випадків це отримає те, що вам потрібно.


3
Якщо встановити режим розриву рядка на "кліп", схоже, зіпсується автоматичне розмір етикетки. Використовуйте UILineBreakModeWordWrapзамість цього.
Niels van der Rest

і краще додайте пробіли "\ n \ n"
djdance

68

Найпростіший підхід за допомогою дошки розгортки

Вставити мітку в StackView і встановити наступні два атрибути StackView в інспекторі атрибутів:

1- Axisдо Horizontal,

2- AlignmentдоTop

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


1
Для кращих результатів ви можете зробити це StackView> Перегляд> UILabel, оскільки коли ви просто вставляєте UILabel всередину StackView, розмір StackView змінює розмір UILabel до мінімального розміру
Daniel Beltrami

Чудова ідея розмістити UILabelв якомусь UIViewпідкласі ( UIViewзвичайно достатньо простого ) і нехай ця мітка росте вниз. Дякую!
Віктор Кучера

1
Дивовижно! Варто також зазначити, що вам потрібно очистити обмеження щодо UILabelта дозволити StackView виконувати свою роботу. Дякую!
Луїз Діас

Якщо команда меню «Редактор-> Вставити» із вибраною вами міткою, обмеження міток очищаються для вас за допомогою Xcode, ви можете просто встановити обмеження для StackView. Цей підхід найбільш близький до планування SwiftUI, тому я його виявив.
Ракетний сад

чому це працює?
Новелізатор

60

Замість цього UILabelви можете використовувати UITextFieldопцію вертикального вирівнювання:

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

7
Caveat: UITextField дозволяє лише один рядок тексту.
Девід Джеймс

2
Щоб завершити коментар @DavidJames: UITextView буде більш підходящим
CedricSoubrie

49

Я довго боровся з цим і хотів поділитися своїм рішенням.

Це дасть вам змогу UILabelавтоматичного зшивання тексту до 0,5 масштабу та вертикального центру центру. Ці параметри також доступні в Storyboard / IB.

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

З-поміж усіх наданих відповідей це було ідеально. Дякую @David Greco Поряд з цими властивостями, якщо ми встановимо налаштуванняFontSizeToFitWidth на 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

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

У програмі Interface Builder

  • Встановити UILabelрозмір найбільшого можливого тексту
  • В Linesінспекторі атрибутів встановіть значення "0"

У своєму коді

  • Встановіть текст етикетки
  • Зателефонуйте sizeToFitна свою етикетку

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

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

працював чудово, за винятком мого випадку, мені потрібно було встановити рядки на 2 - мати UILabel всередині UITableViewCell, а встановлення на 0 зробило це перекриттям, але встановлення на 2 працювало (клітинка має достатньо місця для 2 рядків)
eselk

34

Для адаптивного інтерфейсу (iOS8 або після нього) слід встановити вертикальну вирівнювання UILabel з StoryBoard, змінивши властивості noOfLines= 0 "і

Обмеження

  1. Коригування обмежень UILabel LefMargin, RightMargin та Top Margin.

  2. Зміна Content Compression Resistance Priority For Vertical= 1000` Отже, що вертикаль> горизонталь.

Обмеження UILabel

Відредаговано:

noOfLines=0

і наступних обмежень достатньо для досягнення бажаних результатів.

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


Це було приголомшливо. Чи можете ви пояснити, чому Vertical> Horizontal зробив цю роботу? Дякую.
Габріель

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

Я написав функцію util для досягнення цієї мети. Ви можете поглянути:

// регулюйте висоту багаторядкової мітки, щоб вона вирівнювалась вертикально з верхом
+ (недійсна) alignLabelWithTop: (UILabel *) мітка {
  CGSize maxSize = CGSizeMake (label.frame.size.width, 999);
  label.adjustsFontSizeToFitWidth = НІ;

  // отримати фактичну висоту
  CGSize фактичний розмір = [label.text sizeWithFont: label.font constrainedToSize: maxSize lineBreakMode: label.lineBreakMode];
  CGRect rect = label.frame;
  rect.size.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;
[Утиліти вирівнятиLabelWithTop: lblHello];

Я також написав це у своєму блозі як статтю: http://fstoke.me/blog/?p=2819


27

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

Однак у деяких випадках ми хочемо, щоб мітка займала всі ці пробіли, навіть якщо в мітці є стільки тексту (наприклад, кілька рядків з однаковою висотою).

Тут я використав альтернативний спосіб її вирішення, просто проклавши нові рядки до кінця мітки (pls зауважте, що я насправді успадкував 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 "];
}

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.


15

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

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

Переконайтеся, що кількість нових рядків у рядку призведе до того, що будь-який текст заповнить наявний вертикальний пробіл, і встановіть UILabel для усікання будь-якого переливного тексту.

Тому що іноді досить добре - це досить добре .


Однак, враховуючи різницю у висоті екрану пристроїв iOS, тоді необхідно мати змінну кількість "\ n" для врахування зміни висоти uilabel (що є можливою). Таким чином, цей метод не є особливо корисним довгостроково.
Рамбатіно

Примітка: "швидко і брудно" не "ідеальне рішення на всі часи". Просто говорю'.
Код Роуді

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

Сподіваюся, що хтось допомагає!


14

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


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

Ви, сер, бог!
Diogo Antunes

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

10

Якщо ви використовуєте автоматичний розклад, встановіть вертикальний вмістHuggingPriority на 1000, або в коді, або в IB. У IB вам, можливо, доведеться усунути обмеження висоти, встановивши пріоритет 1, а потім видалити його.


8

Поки ви не виконуєте жодного складного завдання, ви можете використовувати UITextViewзамість цього UILabels.

Вимкнути прокрутку.

Якщо ви хочете, щоб текст відображався повністю просто користувачем sizeToFitта sizeThatFits:методами


8

Швидко,

let myLabel : UILabel!

Щоб ваш текст мітки містився на екрані, він знаходиться вгорі

myLabel.sizeToFit()

Щоб ваш шрифт етикетки підходив до ширини екрана чи певного розміру ширини.

myLabel.adjustsFontSizeToFitWidth = YES

і деякі текстові налаштування для мітки:

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified


Просто зміна синтаксису зі старого синтаксису [myLabel sizeToFit]. Дякую!
вельконут

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.