Wyrównaj tekst pionowo do góry w UILabel

2175

Mam UILabelmiejsce na dwa wiersze tekstu. Czasami, gdy tekst jest zbyt krótki, ten tekst jest wyświetlany w pionowym środku etykiety.

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

obraz reprezentujący UILabel z tekstem wycentrowanym pionowo

Stefan
źródło

Odpowiedzi:

2702

Nie ma sposobu, aby ustawić wyrównanie w pionie na UILabel, ale można uzyskać ten sam efekt, zmieniając ramkę etykiety. Zrobiłem pomarańczowe etykiety, abyś mógł wyraźnie zobaczyć, co się dzieje.

Oto szybki i łatwy sposób to zrobić:

    [myLabel sizeToFit];

sizeToFit, aby wycisnąć etykietę


Jeśli masz etykietę z dłuższym tekstem, która utworzy więcej niż jedną linię, ustaw numberOfLinesna 0(zero tutaj oznacza nieograniczoną liczbę linii).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Dłuższy tekst etykiety za pomocą sizeToFit


Dłuższa wersja

Zrobię etykietę w kodzie, abyś mógł zobaczyć, co się dzieje. Większość tego można również skonfigurować w Konstruktorze interfejsów. Moja konfiguracja to aplikacja oparta na widoku z obrazem tła, który zrobiłem w Photoshopie, aby pokazać marginesy (20 punktów). Etykieta ma atrakcyjny pomarańczowy kolor, dzięki czemu możesz 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];
}

Pewne ograniczenia w użyciu sizeToFitwchodzą w grę z tekstem wyrównanym do środka lub do prawej. Oto co się dzieje:

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

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

wprowadź opis zdjęcia tutaj

Rozmiar etykiety jest nadal ustalony ze stałym lewym górnym rogiem. Możesz zapisać szerokość oryginalnej etykiety w zmiennej i ustawić ją później sizeToFitlub nadać jej 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;

wyrównanie etykiet


Pamiętaj, że sizeToFitprzestrzega minimalnej szerokości początkowej etykiety. Jeśli zaczniesz od etykiety o szerokości 100 i przywołasz sizeToFitją, otrzymasz od niej (prawdopodobnie bardzo wysoką) etykietę o szerokości 100 (lub nieco mniejszej). Przed zmianą rozmiaru możesz ustawić minimalną szerokość etykiety.

Popraw wyrównanie etykiety, zmieniając szerokość ramki

Kilka innych rzeczy do zapamiętania:

To, czy lineBreakModejest przestrzegane, zależy od jego ustawienia. NSLineBreakByTruncatingTail(domyślne) jest później ignorowane sizeToFit, podobnie jak pozostałe dwa tryby obcinania (głowa i środek). NSLineBreakByClippingjest również ignorowane. NSLineBreakByCharWrappingdziała jak zwykle. Szerokość ramki jest nadal zawężona, aby pasowała do skrajnej prawej litery.


Mark Amery naprawił NIB i Storyboardy za pomocą Auto Layout w komentarzach:

Jeśli twoja etykieta jest zawarta w stalówce lub serii ujęć jako widok podrzędny viewViewController, który korzysta z autolayout, wówczas włączenie sizeToFitpołączenia viewDidLoadnie zadziała, ponieważ autolayout rozmiary i pozycje po podviewach viewDidLoadsą wywoływane i natychmiast cofną efekty twojego sizeToFitpołączenie. Jednak dzwonienie sizeToFitod wewnątrz viewDidLayoutSubviews będzie działać.


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

Wykorzystuje tę NSStringmetodę sizeWithFont:constrainedToSize:lineBreakMode:do obliczenia wysokości ramy potrzebnej do dopasowania łańcucha, a 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;
Nevan King
źródło
10
musisz usunąć autolayout
hungbm06
4
Oto prosty sposób rozwiązania problemu: stackoverflow.com/a/26632490/636559
AboulEinein
Jeśli korzystasz z automatycznego układu i scenorysu, dodanie ograniczenia z zaznaczoną opcją „Usuń podczas kompilacji” pomoże
JK ABC
Jeśli potrzebujesz tej pracy, adjustsFontSizeToFitWidthużyj sizeToFit, a następnie ustaw ramkę etykiety na 0, 0, szerokość, wysokość, etykieta.ramka.size.height (która jest nową wysokością określoną przez sizeToFit), a następnie zastosujadjustsFontSizeToFitWidth
Albert Renshaw
3
Ta odpowiedź jest niepotrzebnie skomplikowana i wykorzystuje „słowo wokół”. Zapoznaj się z sugestią poniżej dotyczącą zmiany priorytetu przytulania treści w pionie. Nie wymaga żadnego kodu ...
buraków
335
  1. Ustaw nowy tekst:

    myLabel.text = @"Some Text"
  2. Ustaw maximum numberlinie na 0 (automatycznie):

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

    myLabel.frame = CGRectMake(20,20,200,800)
  4. Zadzwoń, sizeToFitaby zmniejszyć rozmiar ramki, aby zawartość po prostu pasowała:

    [myLabel sizeToFit]

Ramka etykiet jest teraz wystarczająco wysoka i wystarczająco szeroka, aby pasowała do tekstu. Lewy górny róg powinien pozostać niezmieniony. Przetestowałem to tylko z tekstem wyrównanym do lewego górnego rogu. W przypadku innych wyrównań może być konieczne zmodyfikowanie ramki później.

Ponadto moja etykieta ma włączone zawijanie słów.

Jakob Egger
źródło
Cześć Ben, jeszcze raz spojrzałem na mój stary kod i zaktualizowałem odpowiedź, podając niezbędne 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 dla mnie idealnie, 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 wierszy, ale tekst wymaga więcej linii.
Jose Manuel Abarca Rodríguez
159

Odnosząc się do rozwiązania rozszerzenia:

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

Potrzebne jest dodatkowe miejsce w każdej dodanej nowej linii, ponieważ UILabelszwroty karetki iPhone'a wydają się być ignorowane :(

Podobnie alignBottom również powinien zostać zaktualizowany o @" \n@%"miejsce "\n@%"(dla inicjalizacji cyklu należy zastąpić przez „for (int i = 0 ...”).

Działa dla mnie następujące rozszerzenie:

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

Następnie zadzwoń [yourLabel alignTop];lub [yourLabel alignBottom];po każdym przypisaniu tekstu swojego etykiety.

Dmitri Sologoubenko
źródło
@DS Zauważyłem, że dodajesz VerticalAlignmiędzy nawiasami po @implementation UILabel. Będąc nowym w Objective-C, nie spotkałem się wcześniej z tą składnią. Jak to się nazywa?
Julian
Niesamowite, nie wiedziałem o kategoriach, co jeszcze bardziej docenia język Objective-c. Uczenie się więcej języków jest tak ważne, aby zostać dobrym programistą.
JeroenEijkhof
Dał głos. ale iirc to nie zadziała, jeśli nie znasz liczby linii, co jest wadą
Tjirp
Muszę pokazać 3 linie tekstu i wyrównać tekst do góry. Tak więc, czy muszę ustawić liczbę linii na 3. Kiedy ustawię na 3, widzę „...”, jeśli tekst jest mniejszy (mieści się w jednej linii). Jak uniknąć tego „...”
Satyam,
1
Opublikowałem edycję tej kategorii, mam nadzieję, że zostanie ona wkrótce zatwierdzona. Metody sizeWithFont:constrainedToSize:lineBreakMode:i sizeWithFont: są amortyzowane w iOS7. Ponadto ta kategoria działa tylko na etykietach, gdy liczbaOpLini jest większa niż 0.
timgcarlson
146

Na wszelki wypadek komuś pomogłem, miałem ten sam problem, ale mogłem go rozwiązać, przechodząc z używania UILabelna używanie UITextView. Rozumiem, że nie jest to dla wszystkich, ponieważ funkcjonalność jest nieco inna.

Jeśli przełączysz się na używanie UITextView, możesz 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 etykieta.

wprowadź opis zdjęcia tutaj

jowie
źródło
1
Nie jestem pewien, jak wpłynie to na wydajność, jeśli wiele etykiet UIL zostanie przekonwertowanych na widoki tekstu.
ninjaneer
2
Wszystkie inne rozwiązania w tym wątku nie działały dla mnie w iOS 7 (z jakiegokolwiek powodu). Ale zwykłe użycie a UITextViewdało mi od razu pożądany rezultat.
Clifton Labrum,
1
Jest to obejście problemu, wciąż wymaga pewnych poprawek, aby wyglądało autentycznie, ale w rzeczywistości jest to najłatwiejsze rozwiązanie, jakiego nie widziałem, kciuki w górę.
Itai Spector,
1
Podoba mi się to rozwiązanie. To prawda, że ​​mogą występować problemy z wydajnością i takie, w przypadku prostej etykiety na stosunkowo prostym widoku, było to idealne rozwiązanie do tego, czego potrzebowałem (i nie zauważam żadnego wpływu na wydajność)
JCutting8
1
Małą wadą tej metody jest to, że wprowadzi ona dodatkowe wewnętrzne dopełnienie tekstu. Szybkim rozwiązaniem jest wprowadzenie ograniczenia ujemnego.
Sira Lam
122

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 muz, bez Objective-c, bez kłopotó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)
    }

}

Szybki 4.2

class VerticalTopAlignLabel: UILabel {

    override func drawText(in rect:CGRect) {
        guard let labelText = text else {  return super.drawText(in: rect) }

        let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
        var newRect = rect
        newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

        if numberOfLines != 0 {
            newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
        }

        super.drawText(in: newRect)
    }

}
jasongregori
źródło
Szybka wersja działała dla mnie bez żadnych skutków ubocznych! Świetna robota!
l.vasilev
2
Prawdopodobnie najlepsza odpowiedź tutaj, działa we wszystkich scenariuszach. sizeToFit nie działa, jeśli etykieta znajduje się w UITableviewCell. Dobra robota.
rajat chauhan
79

Podobnie jak powyższa odpowiedź, ale nie było to w porządku lub łatwo było spoliczkować kod, więc trochę go wyczyściłem. Dodaj to rozszerzenie do własnego pliku .h i .m lub po prostu 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

Następnie użyj, umieść tekst na etykiecie, a następnie wywołaj odpowiednią metodę, aby go wyrównać:

[myLabel alignTop];

lub

[myLabel alignBottom];
BadPirate
źródło
sizeWithFont przestarzałe
tdios 18.12.19
68

Jeszcze szybszym (i bardziej brudnym) sposobem na osiągnięcie tego jest ustawienie trybu podziału linii UILabel na „Clip” i dodanie stałej liczby nowych 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 łańcucha, jeśli przekracza on liczbę wyświetlanych wierszy, musisz użyć jednego z dłuższe fragmenty kodu - ale w wielu przypadkach dostaniesz to, czego potrzebujesz.

Purple Ninja Girl
źródło
3
Ustawienie trybu podziału linii na „klip” wydaje się zaburzać automatyczną zmianę rozmiaru etykiety. Użyj UILineBreakModeWordWrapzamiast tego.
Niels van der Rest
i lepiej dodaj spacje "\ n \ n"
djdance
68

Najłatwiejsze podejście przy użyciu Storyboard:

Osadź etykietę w StackView i ustaw następujące dwa atrybuty StackView w Inspektorze atrybutów:

1 Axisdo Horizontal,

2- AlignmentdoTop

wprowadź opis zdjęcia tutaj

Baig
źródło
1
Aby uzyskać lepsze wyniki można to zrobić StackView> Widok> UILabel, ponieważ po prostu osadzić UILabel wewnątrz StackView, zmiany rozmiaru StackView aby dopasować UILabel do minimalnej wielkości
Daniel Beltrami
Świetny pomysł, aby umieścić UILabelw jakiejś UIViewpodklasie ( UIViewzwykle wystarcza zwykły ) i pozwolić, aby etykieta rosła w dół. Dzięki!
Viktor Kucera,
1
Niesamowite! Warto też wspomnieć, że trzeba wyraźnych ograniczeń na UILabeli niech StackView wykonywać swoje zadania. Dzięki!
Luiz Dias,
Po wybraniu polecenia Edytor-> Osadzanie z wybraną etykietą ograniczenia etykiet zostaną dla Ciebie usunięte przez Xcode, a następnie możesz po prostu ustawić ograniczenia dla StackView. To podejście jest najbliższe podejściu układowemu „SwiftUI” i tak to odkryłem.
Rocket Garden
dlaczego to działa
Novellizator
60

Zamiast tego UILabelmożesz użyć UITextFieldopcji wyrównania pionowego:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
ivanzoid
źródło
7
Zastrzeżenie: UITextField dopuszcza tylko jeden wiersz tekstu.
David James
2
Aby uzupełnić komentarz @DavidJames: bardziej odpowiedni byłby UITextView
CedricSoubrie
49

Walczyłem z tym przez długi czas i chciałem podzielić się moim rozwiązaniem.

To da ci UILabelautomatyczne odświeżanie 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];
David Greco
źródło
Działa idealnie spośród wszystkich udzielonych odpowiedzi. Dzięki @David Greco. Wraz z tymi właściwościami, jeśli ustawimy adjustsFontSizeToFitWidth na TAK, wówczas tekst będzie pasował do ramki bez modyfikowania ramki etykiety.
Ashok,
43

Utwórz nową klasę

LabelTopAlign

plik .h

#import <UIKit/UIKit.h>


@interface KwLabelTopAlign : UILabel {

}

@end

plik .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

Edytować

Oto prostsza implementacja, która robi to samo:

#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
Sebastian Friedrich
źródło
+1 za prawdziwą odpowiedź. W przeciwieństwie do sizeToFitrozwiązań powoduje to UILabelumieszczenie dowolnego tekstu u góry, nawet jeśli dynamicznie zamieniasz krótszy tekst na dłuższy lub odwrotnie.
SnakE
38

wprowadź opis zdjęcia tutaj

W Konstruktorze interfejsów

  • Ustaw UILabelrozmiar największego możliwego tekstu
  • Ustaw Linesna „0” w Inspektorze atrybutów

W twoim kodzie

  • Ustaw tekst etykiety
  • Zadzwoń sizeToFitna swoją etykietę

Fragment kodu:

self.myLabel.text = @"Short Title";
[self.myLabel sizeToFit];
niedźwiedź
źródło
działało świetnie, z wyjątkiem mojego przypadku musiałem ustawić linie na 2 - mieć UILabel wewnątrz UITableViewCell, a ustawienie na 0 sprawiło, że się nakładają, ale ustawienie na 2 działało (komórka ma wystarczająco dużo miejsca na 2 linie)
eselk
34

W przypadku adaptacyjnego interfejsu użytkownika (iOS8 lub nowszego) wyrównanie w pionie UILabel należy ustawić w StoryBoard, zmieniając właściwości noOfLines= 0` i

Ograniczenia

  1. Dostosowywanie ograniczeń UILabel LefMargin, RightMargin i Top Margin.

  2. Zmień Content Compression Resistance Priority For Vertical= 1000` Tak, że w pionie> w poziomie.

Ograniczenia UILabel

Edytowane:

noOfLines=0

i następujące ograniczenia są wystarczające do osiągnięcia pożądanych rezultatów.

wprowadź opis zdjęcia tutaj

Rabindra Nath Nandi
źródło
To było niesamowite. Czy możesz wyjaśnić, dlaczego praca w pionie> w poziomie? Dzięki.
Gabriel
33

Utwórz podklasę UILabel. Działa jak marzenie:

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

Jak omówiono tutaj .

Martin Wickman
źródło
27

Napisałem funkcję util, aby osiągnąć ten cel. Możesz rzucić okiem:

// dostosuj wysokość etykiety wielowierszowej, aby wyrównała się pionowo do góry
+ (void) alignLabelWithTop: (UILabel *) etykieta {
  CGSize maxSize = CGSizeMake (label.frame.size.width, 999);
  label.adjustsFontSizeToFitWidth = NIE;

  // uzyskać rzeczywistą wysokość
  CGSize actualSize = [label.text sizeWithFont: label.font constrainedToSize: maxSize lineBreakMode: label.lineBreakMode];
  CGRect rect = label.frame;
  rect.size.height = actualSize.height;
  label.frame = rect;
}

.Jak używać? (Jeśli lblHello jest tworzony przez Konstruktora interfejsów, pomijam niektóre szczegóły atrybutów UILabel)

lblHello.text = @ "Witaj świecie! Witaj świecie! Witaj świecie! Witaj świecie! Witaj świecie! Witaj świecie! Witaj świecie! Witaj świecie!";
lblHello.numberOfLines = 5;
[Utils alignLabelWithTop: lblHello];

Napisałem go również na moim blogu jako artykuł: http://fstoke.me/blog/?p=2819

pożar
źródło
27

Poświęciłem trochę czasu na przeczytanie kodu, a także kodu na wprowadzonej stronie, i stwierdziłem, że wszyscy próbują zmodyfikować rozmiar ramki etykiety, aby domyślne wyświetlanie w środku nie było widoczne.

Jednak w niektórych przypadkach chcemy, aby etykieta zajmowała wszystkie te spacje, nawet jeśli etykieta zawiera tak dużo tekstu (np. Wiele wierszy o równej wysokości).

Tutaj użyłem alternatywnego sposobu rozwiązania tego problemu, po prostu wstawiając nowe wiersze na końcu etykiety (proszę zauważyć, że tak naprawdę odziedziczyłem UILabel, ale nie jest to konieczne):

CGSize fontSize = [self.text sizeWithFont:self.font];

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

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

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

for(int i = 0; i < newLinesToPad; i++)
{
    self.text = [self.text stringByAppendingString:@"\n "];
}
Walty Yeung
źródło
22

To, co zrobiłem w mojej aplikacji, to ustawienie właściwości linii UILabel na 0 oraz utworzenie dolnego ograniczenia UILabel i upewnienie się, że jest ustawione na> = 0, jak pokazano na poniższym obrazku. wprowadź opis zdjęcia tutaj

Jack Tiong
źródło
19

Wziąłem tutaj sugestie i stworzyłem widok, który może zawijać UILabel i będzie go dopasowywać i ustawiać liczbę linii, aby był wyrównany do góry. Wystarczy umieścić UILabel jako widok podrzędny:

@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

źródło
19

Możesz użyć TTTAttributLabel , obsługuje wyrównanie w pionie.

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

//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
Andrzej Romanow
źródło
16

Znalazłem, że odpowiedzi na to pytanie są teraz trochę nieaktualne, więc dodaję to dla fanów automatycznych układów.

Auto układ sprawia, że ​​ten problem jest dość trywialny. Zakładając, że dodajemy etykietę UIView *view, następujący kod to osiągnie:

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

Wysokość etykiety zostanie obliczona automatycznie (przy użyciu jej intrinsicContentSize), a etykieta będzie ustawiona od krawędzi do krawędzi poziomo, na górze view.

petehare
źródło
15

Użyłem wielu powyższych metod i chcę tylko dodać szybkie i brudne podejście:

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

Upewnij się, że liczba nowych wierszy w ciągu spowoduje, że dowolny tekst zapełni dostępną przestrzeń pionową, i ustaw UILabel, aby obcinał przepełniony tekst.

Ponieważ czasami wystarczająco dobry jest wystarczająco dobry .

Code Roadie
źródło
Jednak biorąc pod uwagę różnice wysokości ekranów urządzeń z iOS, musiałaby istnieć zmienna liczba „\ n”, aby uwzględnić zmianę wysokości uilabel (co jest możliwe). Zatem ta metoda nie jest szczególnie przydatna w perspektywie długoterminowej.
Rambatino
Uwaga: „szybkie i brudne” nie „idealne rozwiązanie na zawsze”. Tylko mówię'.
Code Roadie
2
@CodeRoadie Szybka i brudna załatwiła sprawę, miałem kilka problemów z układem z prawdziwą odpowiedzią. Mógłbym to zrobić „we właściwy sposób”, ale dziękuję za oszczędność czasu!
Séraphin Hochart
@dGambit Uwaga: „szybki i brudny
Code Roadie
1
Czego nauczyłem się ostatnio podczas dynamicznego ładowania widoków UITextView, że można wywoływać dlopenw celu obsługi wprowadzania tekstu. Może to powodować duże opóźnienie w wątku interfejsu użytkownika, więc to podejście jest znacznie wydajniejsze!
yano
14

Chciałem mieć etykietę, która mogłaby mieć wiele linii, minimalny rozmiar czcionki i wyśrodkowana zarówno w poziomie, jak i w pionie w widoku nadrzędnym. Dodałem programowo moją etykietę do mojego widoku:

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

A potem, gdy chciałem zmienić tekst mojej wytwórni ...

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

Mam nadzieję, że komuś pomoże!

Johnus
źródło
14

FXLabel (na github) robi to od razu po ustawieniu label.contentModena UIViewContentModeTop. Ten komponent nie jest tworzony przeze mnie, ale jest to komponent, z którego często korzystam, ma mnóstwo funkcji i wydaje się działać dobrze.

jjxtra
źródło
11

dla każdego, kto to czyta, ponieważ tekst wewnątrz etykiety nie jest wyśrodkowany pionowo, należy pamiętać, że niektóre typy czcionek nie są projektowane jednakowo. na przykład, jeśli utworzysz etykietę o rozmiarze zapfino 16, zobaczysz, że tekst nie jest idealnie wyśrodkowany w pionie.

jednak praca z helvetica spowoduje wyśrodkowanie tekstu w pionie.

Nir Pengas
źródło
Wiele niestandardowych czcionek nie jest wyrównanych w pionie do środka. UILabel lub UITextView są zawsze wyrównane do środka w pionie. Nie trzeba myśleć o wyrównaniu pionowym w programowaniu iOS.
Amit Singh,
11

Zastosowanie textRect(forBounds:limitedToNumberOfLines:).

class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        super.drawText(in: textRect)
    }
}
ma11hew28
źródło
Pan jest bogiem!
Diogo Antunes,
1
Ta odpowiedź powinna zyskać znacznie więcej głosów, ponieważ jest to zdecydowanie najładniejsze i najczystsze rozwiązanie!
Michael Ochs
10

Podklasę UILabel i ograniczanie prostokąta rysunkowego:

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

    [super drawTextInRect:rect];
}

Wypróbowałem rozwiązanie polegające na dodawaniu nowego wiersza i w niektórych przypadkach napotkałem nieprawidłowe zachowanie. Z mojego doświadczenia wynika, że ​​łatwiej jest ograniczyć rysowanie jak wyżej niż bałagan numberOfLines.

PS Możesz sobie wyobrazić łatwą obsługę UIViewContentMode w ten sposób:

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

    if (self.contentMode == UIViewContentModeTop) {
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }
    else if (self.contentMode == UIViewContentModeBottom) {
        rect.origin.y = MAX(0, rect.size.height - sizeThatFits.height);
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }

    [super drawTextInRect:rect];
}
jrc
źródło
10

Jeśli używasz autolayout, ustaw wartość contentHuggingPriority w pionie na 1000, zarówno w kodzie, jak i IB. W IB może być wtedy konieczne usunięcie ograniczenia wysokości poprzez ustawienie jego priorytetu na 1, a następnie usunięcie go.

phatmann
źródło
8

Dopóki nie wykonujesz żadnego złożonego zadania, możesz użyć UITextViewzamiast niego UILabels.

Wyłącz przewijanie.

Jeśli chcesz, aby tekst był wyświetlany całkowicie, po prostu użytkownik sizeToFiti sizeThatFits:metody

letni znak
źródło
8

W krótkim

let myLabel : UILabel!

Aby tekst etykiety był dopasowany do ekranu i znajduje się na górze

myLabel.sizeToFit()

Aby dopasować czcionkę etykiety do szerokości ekranu lub określonego rozmiaru.

myLabel.adjustsFontSizeToFitWidth = YES

i trochę tekstu Wyrównanie etykiety:

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified

Dara Tith
źródło
Po prostu zmiana składni ze starej składni [myLabel sizeToFit]. Dziękuję Ci!
velkoon
7

To stare rozwiązanie, użyj autolayout na iOS> = 6

Moje rozwiązanie:

  1. Podziel linie samodzielnie (ignorując ustawienia zawijania etykiet)
  2. Rysuj linie samodzielnie (ignorując wyrównanie etykiet)

@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
Sułtan
źródło