Питання Вертикально вирівнювання тексту вгорі всередині UILabel


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

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

image representing a UILabel with vertically-centered text


1985
2018-06-28 08:54


походження




Відповіді:


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

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

    [myLabel sizeToFit];

sizeToFit to squeeze a label


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

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Longer label text with sizeToFit


Довша версія

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

- (void)viewDidLoad
{
    [super viewDidLoad];

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

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

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

    [self.view addSubview:myLabel];
}

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

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

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

enter image description here

Етикетка все ще має фіксований верхній лівий кут. Ви можете зберегти ширину оригінальної мітки у змінній і встановити її після sizeToFit, або надати йому фіксовану ширину, щоб протистояти цим проблемам:

    myLabel.textAlignment = NSTextAlignmentCenter;

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

    CGRect myFrame = myLabel.frame;
    // Resize the frame's width to 280 (320 - margins)
    // width could also be myOriginalLabelFrame.size.width
    myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
    myLabel.frame = myFrame;

label alignment


Зауважте, що sizeToFit буде поважати мінімальну ширину вашої початкової мітки. Якщо ви починаєте з мітки 100 широко і дзвоніть sizeToFit на ній, він поверне вам (можливо дуже високу) етикетку з шириною 100 (або трохи менше). Можливо, ви хочете встановити мітку до потрібної мінімальної ширини, перш ніж змінити розмір.

Correct label alignment by resizing the frame width

Деякі інші речі слід відзначити:

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


Марк Амері надавши виправлення для NIBs та Storyboards, використовуючи Auto Layout в коментарях:

Якщо ваша етикетка включена в пазли або розкадрування як підводне зображення view ViewController, який використовує autolayout, а потім розміщує ваш 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;

2562
2018-06-28 10:36



вам потрібно видалити автозаповнення - hungbm06
Ось простий спосіб вирішити проблему: stackoverflow.com/a/26632490/636559 - AboulEinein
Якщо ви користуєтеся автоматичним макетом та розкадруванням, допоможемо додати обмеження з опцією "Видалити у час створення" - JK ABC
Якщо вам потрібна робота з цим adjustsFontSizeToFitWidth потім використовуйте sizeToFit, потім встановіть рамку мітки на 0, 0, theWidthYouWant, label.frame.size.height (це новий висота, визначена sizeToFit), потім застосовуйте adjustsFontSizeToFitWidth - Albert Renshaw
Ця відповідь є зайвою складністю, і вона використовує "слово навколо". Будь ласка, перегляньте пропозицію нижче, щоб просто змінити вертикальний зміст, який обмінюється пріоритетом. Не вимагає коду ... - beetree


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

    myLabel.text = @"Some Text"
    
  2. Встановіть maximum number ліній до 0 (автоматично):

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

    myLabel.frame = CGRectMake(20,20,200,800)
    
  4. Зателефонувати sizeToFit щоб зменшити розмір кадру, щоб вміст просто підходив:

    [myLabel sizeToFit]
    

Ярлик кадрів тепер просто високий і широкий, щоб відповідати вашому тексту. Верхній лівий слід не змінювати. Я протестував це лише з вирівняним верхнім літером тексту. Для інших вирівнювання, можливо, доведеться модифікувати кадр пізніше.

Крім того, мій ярлик увімкнув слово обгортання.


315
2017-08-27 16:24



Гей Бен, я просто переглянув свій старий код і оновив мій відповідь з потрібними кроками. - Jakob Egger
Це добре працювало для мене. Я встановлюю розміри мого UILabel і змінив numberOfLines на 0 в IB, а потім після налаштування тексту з назвою [myLabel sizeToFit]. - Dan Ellis
працює відмінно для мене, після встановлення кількості ліній у Attr. Інспектор - d2burke
Не працює в разі кількості рядків більше 1 - Tom
Це не спрацьовує, якщо ви хочете мітки з двох рядків, але для тексту потрібно більше рядків. - Jose Manuel Abarca Rodríguez


Зверніться до розширення розширення:

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


151
2017-09-09 13:01



@ D.S. Я помітив, що ви додаєте VerticalAlign між дужками після @implementation UILabel. Будучи новим для Objective-C, я не пробіг через цей синтаксис раніше. Що це називається? - Julian
Дивовижна, не знала про категорії, це дає мені ще більшу вдячність за мову Objective-c. Вивчення більше мов так важливо, щоб стати хорошим програмістом. - JeroenEijkhof
Отримав високий результат. але це не спрацює, якщо ви не знаєте кількість рядків, що є недоліком - Tjirp
Я повинен показати 3 рядки тексту та вирівняти текст до верху. Отже, я повинен встановити кількість рядків до 3. Коли я встановлюю його на 3, я бачу "...", якщо текст менший (що вписується в один рядок). Як уникнути цього "..." - Satyam
У мене лінії встановлені на 2, але це не робить різниці для alignTop або alignBottom, все, що мені не вистачає? label = [[UILabel alloc] init]; label.numberOfLines = 2; label.frame = CGRectMake (0, TABS_HEIGHT-25, self.frame.size.width, 25); label.font = [UIFont fontWithName: @ "Arial" розмір: 10]; label.textAlignment = UITextAlignmentCenter; [label alignTop]; - htafoya


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

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

enter image description here


126
2017-10-05 12:46



Не знаєте, як це вплине на ефективність, якщо багато UILabels перетворені в текстові перегляди. - ninjaneer
Всі інші рішення цієї теми не працювали для мене в iOS 7 (з якоїсь причини). Але просто використовуючи a UITextView дав мені потрібний результат відразу. - Clifton Labrum
Це обхідний шлях, він все ще потребує деяких коригувань, щоб зробити його справжнім, але насправді це найпростіше рішення з коду, яке я бачив, підстрибуючи. - Itai Spector
Мені подобається це рішення. Отримано, можуть бути проблеми з продуктивністю і такі, для простої мітки на відносно простому перегляді, це було ідеально підходить для того, що мені потрібно (і я не помічаю ніяких ефектів продуктивності) - JCutting8
Невеликий недолік цього методу полягає в тому, що він внесе додатковий внутрішній простір до тексту. Швидкий виправлення - це ввести негативне обмеження. - Sira Lam


Ні мес, ні суєти

@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

Ні місі, ні Об'єктив-с, ні суєти, а Свіфт 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)
    }

}

80
2018-05-21 07:47



Швидка версія працювала для мене без будь-яких побічних ефектів! Чудова робота! - l.vasilev
Напевно, найкраща відповідь тут, працює у всіх сценаріях. sizeToFit не працює, якщо мітка знаходиться в UITableviewCell. Гарна робота. - rajat chauhan
Працює прекрасна людина - anoop4real


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

#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end


@implementation UILabel (VerticalAlign)
- (void)alignTop
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

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

- (void)alignBottom
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

    for(int i=0; i< newLinesToPad; i++)
    {
        self.text = [NSString stringWithFormat:@" \n%@",self.text];
    }
}
@end

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

[myLabel alignTop];

або

[myLabel alignBottom];

76
2018-03-04 02:44



Хто-небудь хоче додати версію Swift 3? - Duncan Groenewald


Ще швидше (і брудніше) спосіб це зробити, встановивши режим розриву лінії UILabel на «Clip» і додавши фіксовану кількість нових рядків.

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

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


66
2018-04-18 21:08



Встановлення режиму розриву рядка на "кліп", здається, перешкоджає автоматичному вибору мітки. Використовуйте UILineBreakModeWordWrap замість цього. - Niels van der Rest
і краще додати пробіли "\ n \ n" - djdance


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

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

56
2018-02-10 10:36



Застереження: UITextField дозволяє використовувати лише один рядок тексту. - David James
Щоб завершити @DavidJames коментар: UITextView буде більш доречним - CedricSoubrie


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

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

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

47
2017-10-16 16:38



Він працював ідеально серед всіх наданих відповідей. Дякуємо @David Greco. Окрім цих властивостей, якщо ми встановили параметри FontSizeToFitWidth на YES, текст вписується в кадр, не змінюючи фрейм етикетки. - Ashok Kumar S
Чи можете ви розробити? Я не отримав синтаксис, а також, як ми можемо встановити це ж у builder у storyboard - Sasuke Uchiha


Створіть новий клас

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
2017-08-04 08:07



+1 для реальної відповіді. На відміну від sizeToFit рішення, які це дійсно робить UILabel покласти будь-який текст зверху, навіть якщо ви динамічно замінюєте коротший текст довшим або навпаки. - SnakE
дивовижно, саме те, що мені потрібно. Може, будь ласка, скажіть мені, як це зробити, щоб вирівняти текст. Дякуємо - Pratyusha Terli


enter image description here

В інтерфейсі Builder

  • Набір UILabel до розміру максимально можливого тексту
  • Набір Lines до "0" в Інспектор атрибутів

У вашому коді

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

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

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

38
2018-01-03 04:40



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