Pytanie Wyrównaj w pionie tekst do góry w ramach protokołu UILabel


mam UILabel z miejscem na dwa wiersze tekstu. Czasami, gdy tekst jest zbyt krótki, tekst jest wyświetlany w pionowym środku etykiety.

Jak wyrównać tekst w pionie, aby zawsze znajdował się na górze UILabel?

image representing a UILabel with vertically-centered text


1986
2018-06-28 08:54


pochodzenie




Odpowiedzi:


Nie ma możliwości ustawienia pionowego wyrównania na a UILabel, ale możesz uzyskać ten sam efekt, zmieniając ramkę etykiety. Zrobiłem moje etykiety na pomarańczowo, abyś mógł wyraźnie zobaczyć, co się dzieje.

Oto szybki i łatwy sposób na zrobienie tego:

    [myLabel sizeToFit];

sizeToFit to squeeze a label


Jeśli masz etykietę z dłuższym tekstem, który będzie zawierał więcej niż jedną linię, ustaw numberOfLines do 0 (zero tutaj oznacza nieograniczoną liczbę linii).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Longer label text with sizeToFit


Wersja dłuższa

Zrobię moją etykietę w kodzie, żebyś mógł zobaczyć, co się dzieje. Większość z tego można skonfigurować również w narzędziu do tworzenia interfejsu. Moja konfiguracja to aplikacja oparta na widoku z obrazem tła wykonanym w Photoshopie, aby pokazać marginesy (20 punktów). Etykieta ma atrakcyjny pomarańczowy kolor, dzięki czemu można zobaczyć, co się dzieje z wymiarami.

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

Niektóre ograniczenia użytkowania sizeToFit wejdź w grę z tekstem wyśrodkowanym lub wyrównanym do prawej. Oto co się dzieje:

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

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

enter image description here

Etykieta ma nadal rozmiar ze stałym lewym górnym rogiem. Możesz zapisać szerokość oryginalnej etykiety w zmiennej i ustawić ją później sizeToFitlub nadaj mu stałą szerokość, aby przeciwdziałać tym problemom:

    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


Zauważ, że sizeToFit będzie przestrzegać minimalnej szerokości początkowej etykiety. Jeśli zaczniesz od etykiety 100 szerokiej i zadzwoń sizeToFit na nim otrzymasz (prawdopodobnie bardzo wysoką) etykietę o szerokości 100 (lub nieco mniejszej). Przed zmianą rozmiaru możesz ustawić etykietę na minimalną szerokość.

Correct label alignment by resizing the frame width

Kilka innych rzeczy do zapamiętania:

Czy lineBreakMode jest szanowany, zależy od tego, jak jest ustawiony. NSLineBreakByTruncatingTail (domyślnie) jest ignorowane po sizeToFit, podobnie jak pozostałe dwa tryby obcinania (głowa i środek). NSLineBreakByClipping jest również ignorowane. NSLineBreakByCharWrapping działa jak zwykle. Szerokość ramki jest nadal zwężona, aby dopasować ją do litery znajdującej się najbardziej na prawo.


Mark Amery poprawiono NIB i storyboardy za pomocą Auto Layout w komentarzach:

Jeśli Twoja etykieta jest dołączona do końcówki lub scenorysu jako podzbiór pliku view kontrolera ViewController, który używa autolayout, a następnie umieszczenie twojego sizeToFit Zadzwoń pod viewDidLoad nie zadziała, ponieważ rozmiary autolayout i pozycje subviews po viewDidLoad jest wywoływana i natychmiast cofnie efekty twojego sizeToFit połączenie. Jednak dzwonienie sizeToFit z wewnątrz viewDidLayoutSubviews  będzie praca.


Moja oryginalna odpowiedź (dla potomności / odniesienia):

To wykorzystuje NSString metoda sizeWithFont:constrainedToSize:lineBreakMode: aby obliczyć wysokość ramki potrzebną do dopasowania łańcucha, następnie ustawia początek i szerokość.

Zmień rozmiar ramki etykiety, używając tekstu, który chcesz wstawić. W ten sposób możesz pomieścić dowolną liczbę linii.

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;

2563
2018-06-28 10:36



musisz usunąć autolayout - hungbm06
Oto prosty sposób na rozwiązanie problemu: stackoverflow.com/a/26632490/636559 - AboulEinein
Jeśli korzystasz z automatycznego układu i storyboardu, pomocne będzie dodanie ograniczenia z opcją "Usuń podczas kompilacji" - JK ABC
Jeśli potrzebujesz tej pracy z adjustsFontSizeToFitWidth następnie użyj sizeToFit, a następnie ustaw ramkę etykiety na 0, 0, theWidthYouWant, label.frame.size.height (która jest nową wysokością określoną przez sizeToFit). adjustsFontSizeToFitWidth - Albert Renshaw
Ta odpowiedź jest niepotrzebnie skomplikowana i używa słowa "dookoła". Zapoznaj się z poniższą sugestią po prostu zmieniając priorytet priorytetowy dla treści w pionie. Nie wymaga żadnego kodu ... - beetree


  1. Ustaw nowy tekst:

    myLabel.text = @"Some Text"
    
  2. Ustaw maximum number linii do 0 (automatycznie):

    myLabel.numberOfLines = 0
    
  3. Ustaw ramkę etykiety na maksymalny rozmiar:

    myLabel.frame = CGRectMake(20,20,200,800)
    
  4. Połączenie sizeToFit aby zmniejszyć rozmiar ramki, aby zawartość pasowała do:

    [myLabel sizeToFit]
    

Ramka z etykietami jest teraz wystarczająco wysoka i wystarczająco szeroka, aby zmieściła się w tekście. Górny lewy powinien pozostać niezmieniony. Przetestowałem to tylko z tekstem wyrównanym do lewej górnej strony. W przypadku innych linii trasowania konieczne może być później zmodyfikowanie ramki.

Moja etykieta ma włączone zawijanie słów.


315
2017-08-27 16:24



Hej, Ben, ponownie spojrzałem na mój stary kod i zaktualizowałem moją odpowiedź, wykonując właściwe kroki. - Jakob Egger
To działało dobrze dla mnie. Ustawiłem wymiary mojego UILabel i zmieniłem numberOfLines na 0 w IB, a następnie po ustawieniu tekstu o nazwie [myLabel sizeToFit]. - Dan Ellis
działa idealnie dla mnie, po ustawieniu liczby linii w Attr. Inspektor - d2burke
Nie działa w przypadku liczby linii większej niż 1 - Tom
Nie działa, gdy chcesz mieć etykietę dwóch linii, ale tekst wymaga więcej linii. - Jose Manuel Abarca Rodríguez


Odnosząc się do rozwiązania rozszerzeń:

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

powinien zostać zastąpiony przez

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

Dodatkowe miejsce jest potrzebne w każdym dodanym znaku nowej linii, ponieważ iPhone UILabels"końcowe znaki powrotu karetki wydają się być ignorowane :(

Podobnie, alignBottom powinien być również zaktualizowany za pomocą @" \n@%" zamiast "\n@%" (Inicjalizacja cyklu musi być zastąpiona przez "for (int i = 0 ...").

Poniższe rozszerzenie działa dla mnie:

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

Wtedy zadzwoń [yourLabel alignTop]; lub [yourLabel alignBottom]; po każdym przypisaniu tekstu yourLabel.


151
2017-09-09 13:01



@ D.S. Zauważyłem, że dodajesz VerticalAlign między nawiasami po @implementation UILabel. Będąc nowicjuszem w Objective-C, nie natknąłem się wcześniej na tę składnię. Jak to się nazywa? - Julian
Niesamowite, nie wiedziałem o kategoriach, to daje mi jeszcze więcej uznania dla języka Objective-c. Uczenie się większej liczby języków jest tak ważne, aby stać się dobrym programistą. - JeroenEijkhof
Wygrał uprowadzenie. ale iirc to nie zadziała, jeśli nie znasz liczby linii, która jest wadą - Tjirp
Muszę pokazać 3 wiersze tekstu i wyrównać tekst do góry. Czy muszę ustawić liczbę linii na 3. Kiedy ustawię ją na 3, widzę "...", jeśli tekst jest mniejszy (pasuje do jednej linii). Jak tego uniknąć "..." - Satyam
Mam linie ustawione na 2, ale nie robi to różnicy dla alignTop lub alignBottom, czego mi brakuje? label = [[UILabel alloc] init]; label.numberOfLines = 2; label.frame = CGRectMake (0, TABS_HEIGHT-25, self.frame.size.width, 25); label.font = [UIFont fontWithName: @ "Arial" rozmiar: 10]; label.textAlignment = UITextAlignmentCenter; [etykieta alignTop]; - htafoya


Na wszelki wypadek, to dla nikogo, miałem ten sam problem, ale byłem w stanie rozwiązać problem po prostu przełączając się z użycia UILabel używać UITextView. Doceniam to nie dla wszystkich, ponieważ funkcjonalność jest nieco inna.

Jeśli przełączysz się na używanie UITextViewmożna wyłączyć wszystkie właściwości widoku przewijania, a także włączoną interakcję użytkownika ... To zmusi go do działania bardziej jak etykiety.

enter image description here


126
2017-10-05 12:46



Nie wiem, jak to wpłynie na wydajność, jeśli wiele UILabeli jest konwertowanych na widoki tekstowe. - ninjaneer
Wszystkie inne rozwiązania tego wątku nie działały dla mnie w iOS 7 (z jakiegokolwiek powodu). Ale po prostu używając a UITextView natychmiast dał mi pożądany rezultat. - Clifton Labrum
To obejście problemu, nadal wymaga pewnych korekt, aby wyglądało na autentyczne, ale w rzeczywistości jest to najprostsze rozwiązanie bez kodu, które widziałem, kciuk w górę. - Itai Spector
Podoba mi się to rozwiązanie. Oczywiście, mogą występować problemy z wydajnością i takie, w przypadku prostego oznaczenia na stosunkowo prostym widoku, jest to idealne rozwiązanie dla tego, czego potrzebowałem (i nie widzę żadnego wpływu na wydajność) - JCutting8
Niewielką wadą tej metody jest to, że wprowadza ona dodatkowe wewnętrzne wypełnienie tekstu. Szybką poprawką jest wprowadzenie ujemnego ograniczenia. - Sira Lam


Bez musów, bez zamieszania

@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

Bez musów, bez Celu-c, bez problemów, ale 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)
    }

}

80
2018-05-21 07:47



Wersja szybka działała dla mnie bez żadnych skutków ubocznych! Świetna robota! - l.vasilev
Prawdopodobnie najlepsza odpowiedź tutaj, działa we wszystkich scenariuszach. sizeToFit nie działa, jeśli etykieta jest w widoku UITableviewCell. Dobra robota. - rajat chauhan
Działa wspaniale - anoop4real


Podobnie jak w powyższej odpowiedzi, ale nie było to w porządku, lub łatwo wskoczyć w kod, więc trochę go posprzątałem. Dodaj to rozszerzenie do własnego pliku .h i .m lub wklej bezpośrednio nad implementacją, której zamierzasz użyć:

#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

A następnie użyć, umieścić swój tekst na etykiecie, a następnie wywołać odpowiednią metodę, aby go wyrównać:

[myLabel alignTop];

lub

[myLabel alignBottom];

76
2018-03-04 02:44



Czy ktoś chce dodać wersję Swift 3 tego? - Duncan Groenewald


Jeszcze szybszym (i bardziej brudnym) sposobem na osiągnięcie tego celu jest ustawienie trybu podziału linii UILabel na "Clip" i dodanie ustalonej liczby znaków nowej linii.

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

To rozwiązanie nie będzie działać dla wszystkich - w szczególności, jeśli nadal chcesz wyświetlać "..." na końcu ciągu, jeśli przekracza on liczbę wyświetlanych linii, musisz użyć jednego z dłuższe fragmenty kodu - ale w wielu przypadkach dostaniesz to, czego potrzebujesz.


66
2018-04-18 21:08



Ustawienie trybu podziału linii na "klip" wydaje się zepsuć automatyczne dopasowywanie etykiety. Posługiwać się UILineBreakModeWordWrap zamiast. - Niels van der Rest
i lepiej dodaj spacje "\ n \ n" - djdance


Zamiast UILabel możesz użyć UITextField który ma opcję pionowego wyrównania:

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

56
2018-02-10 10:36



Zastrzeżenie: UITextField pozwala tylko na pojedynczy wiersz tekstu. - David James
Aby zakończyć komentarz @DavidJames: UITextView byłby bardziej odpowiedni - CedricSoubrie


Długo się z tym zmagałem i chciałem podzielić się moim rozwiązaniem.

To da ci UILabel to spowoduje automatyczne zmniejszenie tekstu do 0,5 skali i wyśrodkowanie tekstu w pionie. Te opcje są również dostępne w Storyboard / IB.

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

47
2017-10-16 16:38



Sprawdził się doskonale we wszystkich dostarczonych odpowiedziach. Dzięki @David Greco. Wraz z tymi właściwościami, jeśli ustawimy adjustsFontSizeToFitWidth na YES, tekst będzie pasował do ramki bez modyfikowania ramki etykiety. - Ashok Kumar S
Czy potrafisz opracować? Nie dostałem składni, a także w jaki sposób możemy ustawić to samo w konstruktorze ui storyboard - Sasuke Uchiha