UILabel içinde metni dikey olarak yukarı hizala


2175

UILabelİki metin satırı için bir boşluk var . Bazen, metin çok kısa olduğunda, bu metin etiketin dikey merkezinde görüntülenir.

Metni her zaman en üst kısmında olacak şekilde dikey olarak nasıl hizalayabilirim UILabel?

dikey olarak ortalanmış metin içeren bir UILabel'i temsil eden resim

Yanıtlar:


2702

A üzerinde dikey hizalamayı ayarlamanın bir yolu yoktur UILabel, ancak etiketin çerçevesini değiştirerek aynı efekti elde edebilirsiniz. Neler olduğunu net bir şekilde görebilmeniz için etiketlerimi turuncu yaptım.

İşte bunu yapmanın hızlı ve kolay yolu:

    [myLabel sizeToFit];

Bir etiketi sıkıştırmak için sizeToFit


Birden fazla satır, set yapacak daha uzun metin ile bir etiketi varsa numberOfLinesiçin 0(sıfır burada çizgilerin sınırsız sayıda anlamına gelir).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

SizeToFit ile daha uzun etiket metni


Daha Uzun Versiyon

Neler olup bittiğini görebilmek için etiketimi kodda yapacağım. Bunların çoğunu Arayüz Oluşturucu'da da ayarlayabilirsiniz. Kurulumum, kenar boşluklarını göstermek için Photoshop'ta yaptığım arka plan görüntüsüne sahip Görünüm Tabanlı bir Uygulama (20 puan). Etiket çekici bir turuncu renktedir, böylece boyutlarda neler olduğunu görebilirsiniz.

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

Bazı kullanım kısıtlamaları sizeToFitorta veya sağa hizalanmış metinle devreye girer. İşte olanlar:

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

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

resim açıklamasını buraya girin

Etiket hala sabit bir sol üst köşeyle boyutlandırılmıştır. Orijinal etiketin genişliğini bir değişkene kaydedip daha sonra ayarlayabilirsiniz sizeToFitveya bu sorunlara karşı koymak için sabit bir genişlik verebilirsiniz:

    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;

etiket hizalama


Not sizeToFitİlk etiketin asgari genişliğini saygı duyacağım. 100 genişliğinde bir etiketle başlarsanız ve sizeToFitonu çağırırsanız , size 100 (veya biraz daha az) genişlikte (muhtemelen çok uzun) bir etiket verir. Yeniden boyutlandırmadan önce etiketinizi istediğiniz minimum genişliğe ayarlamak isteyebilirsiniz.

Çerçeve genişliğini yeniden boyutlandırarak etiket hizalamasını düzeltin

Dikkat edilmesi gereken başka şeyler:

Uyulup lineBreakModeuyulmadığı, nasıl ayarlandığına bağlıdır. NSLineBreakByTruncatingTail(varsayılan) sizeToFitve diğer iki kesme modu (baş ve orta) sonra dikkate alınmaz . NSLineBreakByClippingde yok sayılır. NSLineBreakByCharWrappingher zamanki gibi çalışır. Çerçeve genişliği hala en sağdaki harfe sığacak şekilde daraltılmıştır.


Mark Amery , yorumlarda Otomatik Mizanpaj kullanan NIB'ler ve Storyboard'lar için bir düzeltme verdi:

Etiketiniz, otomatik yerleşim viewkullanan bir ViewController öğesinin alt görünümü olarak bir uç veya film şeridine sizeToFitdahil viewDidLoadedilirse, otomatik yerleşim, alt görünümleri arandıktan sonra boyutlandırır ve konumlandırdığından aramanızı koymak işe yaramaz viewDidLoad. sizeToFitaramak. Ancak çağırarak sizeToFitiçinden viewDidLayoutSubviews irade çalışmaları.


Orijinal Cevabım (gelecek / referans için):

Bu, bir dizeye sığdırmak için gereken kare yüksekliğini hesaplamak için NSStringyöntemi kullanır sizeWithFont:constrainedToSize:lineBreakMode:, ardından başlangıç ​​noktasını ve genişliği ayarlar.

Eklemek istediğiniz metni kullanarak etiketin çerçevesini yeniden boyutlandırın. Bu şekilde istediğiniz sayıda çizgiyi barındırabilirsiniz.

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 kaldırmak gerekir
hungbm06 22:14

4
Sorunu çözmenin basit bir yolu: stackoverflow.com/a/26632490/636559
AboulEinein

Otomatik düzen ve film şeridi kullanıyorsanız, "Oluşturma zamanında kaldır" seçeneği işaretli bir kısıtlama eklemek yardımcı olacaktır
JK ABC

Bunu yapmak için size ihtiyacınız varsa adjustsFontSizeToFitWidthsizeToFit kullanın ve ardından etiketin çerçevesini 0, 0 olarak ayarlayın, WididYouWant, label.frame.size.height (sizeToFit tarafından belirlenen yeni yüksekliktir) SONRA geçerlidiradjustsFontSizeToFitWidth
Albert Renshaw

3
Bu cevap gereksiz yere karmaşık ve bir "kelime" kullanıyor. Lütfen yalnızca dikey içerik sarılma önceliğini değiştirme önerisine bakın. Herhangi bir kod gerektirmez ...
beetree

335
  1. Yeni metni ayarlayın:

    myLabel.text = @"Some Text"
  2. Set maximum number(otomatik) 0 hatları:

    myLabel.numberOfLines = 0
  3. Etiketin çerçevesini maksimum boyuta ayarlayın:

    myLabel.frame = CGRectMake(20,20,200,800)
  4. Çağrı sizeToFitiçeriği sadece uyacak şekilde kare boyutunu azaltmak için:

    [myLabel sizeToFit]

Etiketler çerçevesi artık metninize sığacak kadar yüksek ve geniş. Sol üst kısım değiştirilmemelidir. Bunu sadece sola hizalanmış metinle test ettim. Diğer hizalamalar için daha sonra çerçeveyi değiştirmeniz gerekebilir.

Ayrıca, etiketimde sözcük kaydırma özelliği etkin.


Hey Ben, eski koduma tekrar baktım ve cevabımı gerekli doğru adımlarla güncelledim.
Jakob Egger

Bu benim için iyi çalıştı. Ben UILabel benim boyutları ayarlamak ve sayıOfLines IB ve 0 sonra [myLabel sizeToFit] adlı metni ayarladıktan sonra değiştirdi.
Dan Ellis

Attr'daki satır sayısını ayarladıktan sonra benim için mükemmel çalışıyor. Müfettiş
d2burke

1'den fazla satır sayısı durumunda çalışmaz
Tom

İki satırlık bir etiket istediğinizde çalışmaz, ancak metin daha fazla satır gerektirir.
Jose Manuel Abarca Rodríguez

159

Genişletme çözümüne bakıldığında:

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

ile değiştirilmelidir

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

Eklenen her yeni satırda ek alan gerekir, çünkü iPhone'un UILabelssondaki satırbaşı geri döndürülüyormuş gibi görünüyor :(

Benzer şekilde, alignBottom @" \n@%"yerine de ile güncellenmelidir "\n@%"(döngü başlatma için "for (int i = 0 ..." ile de değiştirilmelidir).

Aşağıdaki uzantı benim için çalışıyor:

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

Sonra ara [yourLabel alignTop];[yourLabel alignBottom]; her bir Etiket metin atamanızı veya sonra .


@DS Sonra VerticalAlignparantez arasına eklediğinizi fark ettim @implementation UILabel. Objective-C için yeni olarak daha önce bu sözdizimi ile karşılaşmadım. Buna ne denir?
Julian

Şaşırtıcı, kategorileri bilmiyordum, bu bana Objective-c dili için daha fazla takdir veriyor. İyi bir programcı olmak için daha fazla dil öğrenmek çok önemlidir.
JeroenEijkhof

Bir oy verin. ama bir dezavantajı olan satır sayısını bilmiyorsanız bu
iirc çalışmaz

Ben 3 satır metin göstermek ve metni üste hizalamak gerekir. Yani, 3 için satır sayısını ayarlamak zorunda mıyım. 3 olarak ayarladığımda, metin daha az ise (... bir satıra sığar) "..." görüyorum. Bu "..." önlemek için
Satyam

1
Bu kategoride bir düzenleme yayınladım, umarım kısa süre içinde onaylanır. Yöntemler sizeWithFont:constrainedToSize:lineBreakMode:ve sizeWithFont: iOS7'de her ikisi de amortismana tabi tutulur. Ayrıca, bu kategori yalnızca numberOfLines 0'dan büyük olduğunda etiketler üzerinde çalışır
timgcarlson

146

Sadece kimseye herhangi bir yardım var bu durumda, ben de aynı problem vardı ama sadece kullanarak geçiş yaparak sorunu çözmeyi başardı UILabelkullanmadan UITextView. Bu, herkes için değil, çünkü işlevsellik biraz farklı olduğunu takdir ediyorum.

Kullanıma geçiş UITextViewyaparsanız, Kullanıcı Etkileşimi Etkin'in yanı sıra tüm Kaydırma Görünümü özelliklerini de kapatabilirsiniz ... Bu, onu daha çok etiket gibi davranmaya zorlar.

resim açıklamasını buraya girin


1
Birçok UILabel metin görünümüne dönüştürülürse bunun performansı nasıl etkileyeceğinden emin değilim.
ninjaneer

2
Bu konudaki diğer tüm çözümler iOS 7'de benim için işe yaramadı (herhangi bir nedenle). Ama sadece bir kullanarak UITextViewbana hemen istenen sonucu verdi.
Clifton

1
Bu bir çözüm, otantik görünmesini sağlamak için hala bazı ayarlamalar gerekiyor, ama gerçekten bu gördüğüm en kolay hiçbiri kod çözümü, başparmak yukarıya.
Itai Spector

1
Bu çözümü seviyorum. Verilen, performans sorunları olabilir ve nispeten basit bir görünümde basit bir etiket için, bu ihtiyacım olan şey için mükemmeldi (ve herhangi bir performans etkisi fark edemiyorum)
JCutting8

1
Bu yöntemin küçük bir dezavantajı, metne ekstra iç dolgu sunmasıdır. Hızlı bir düzeltme, olumsuz bir kısıtlama getirmektir.
Sira Lam

122

Karışıklık yok, karışıklık yok

@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

Kargaşa yok, Objective-c yok, karışıklık yok ama 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)
    }

}

Hızlı 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)
    }

}

Hızlı sürüm hiçbir yan etkisi olmadan benim için çalıştı! Harika iş!
l.vasilev

2
Muhtemelen buradaki en iyi cevap, tüm senaryolarda çalışır. Etiket UITableviewCell'de ise sizeToFit çalışmaz. İyi iş.
rajat chauhan

79

Yukarıdaki cevap gibi, ama oldukça doğru değildi veya kod içine tokatlamak kolay değildi, bu yüzden biraz temizledim. Bu uzantıyı kendi .h ve .m dosyasına ekleyin veya kullanmak istediğiniz uygulamanın hemen üstüne yapıştırın:

#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

Ardından kullanmak için metninizi etikete yerleştirin ve ardından hizalamak için uygun yöntemi çağırın:

[myLabel alignTop];

veya

[myLabel alignBottom];

sizeWithFont kullanımdan kaldırıldı
tdios

68

Bunu başarmanın daha hızlı (ve daha kirli) bir yolu, UILabel'in satır sonu modunu "Klip" olarak ayarlamak ve sabit miktarda yeni satır eklemektir.

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

Bu çözüm herkes için işe yaramaz - özellikle, dizenizin sonunda hala gösterdiğiniz satır sayısını aşarsa "..." göstermek istiyorsanız, aşağıdakilerden birini kullanmanız gerekir: daha uzun kod parçaları - ancak çoğu durumda bu size ihtiyacınız olanı alır.


3
Satır sonu modunu 'klip' ​​olarak ayarlamak, etiketin otomatik boyutlandırmasını bozuyor gibi görünüyor. UILineBreakModeWordWrapBunun yerine kullanın .
Niels van der Rest

ve daha iyi boşluklar ekleyin "\ n \ n"
djdance

68

Storyboard'u kullanarak en kolay yaklaşım:

Göm Etiket içinde StackView Özellik Inspector'da StackView iki nitelikler aşağıdaki ve seti:

1- AxisilaHorizontal ,

2- AlignmentilaTop

resim açıklamasını buraya girin


1
Daha iyi sonuçlar için bunu StackView> Görünüm> UILabel yapabilirsiniz, çünkü bir StackPack içine bir UILabel gömdüğünüzde, StackView yeniden boyutlandırılır UILabel sığdırmak için boyutlandırma
Daniel Beltrami

UILabelBazı UIViewalt sınıflara yerleştirmek ( UIViewgenellikle yeterlidir) ve etiketin aşağı yönde büyümesine izin vermek için harika bir fikir . Teşekkürler!
Viktor Kucera

1
Müthiş! Bu size gereken de bahsetmemiz var net kısıtlamalar üzerinde UILabelve StackView işini yapsın. Teşekkürler!
Luiz Dias

Editör-> Yerleştir menü komutu etiketiniz seçiliyken, etiket kısıtlamaları sizin için Xcode tarafından temizlenir, ardından StackView için kısıtlamaları ayarlayabilirsiniz. Bu yaklaşım, 'SwiftUI' düzeni yaklaşımına en yakın olanı, bunu nasıl keşfettim.
Rocket Garden

bu neden çalışıyor?
Novellizator

60

Bunun yerine dikey hizalama seçeneğine sahip olanları UILabelkullanabilirsiniz UITextField:

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

7
Uyarı: UITextField yalnızca tek bir metin satırına izin verir.
David James

2
@DavidJames yorumunu tamamlamak için: UITextView daha uygun olurdu
CedricSoubrie

49

Uzun zamandır bununla mücadele ettim ve çözümümü paylaşmak istedim.

Bu, UILabelmetni 0,5 ölçeğe otomatik olarak çekecek ve metni dikey olarak ortalayacaktır. Bu seçenekler Storyboard / IB'de de mevcuttur.

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

Verilen tüm cevaplar arasında mükemmel çalıştı. Teşekkürler @ David Greco. Bu özelliklerle birlikte, adjustSFontSizeToFitWidth öğesini YES olarak ayarlarsak, metin etiketin çerçevesini değiştirmeden çerçeveye sığar.
Ashok

43

Yeni bir sınıf oluştur

LabelTopAlign

.h dosyası

#import <UIKit/UIKit.h>


@interface KwLabelTopAlign : UILabel {

}

@end

.m dosyası

#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

Düzenle

İşte aynı şeyi yapan daha basit bir uygulama:

#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

Gerçek cevap için +1. sizeToFitÇözümlerden farklı olarak, daha kısa bir metni dinamik olarak daha uzun bir metinle değiştirseniz veya tam tersi olsa bile, aslında herhangi bir metni üste UILabelkoyar .
SnakE

38

resim açıklamasını buraya girin

Arayüz Oluşturucu'da

  • UILabelMümkün olan en büyük Metnin boyutuna ayarla
  • LinesÖzellik Denetçisinde '0' olarak ayarlayın

Kodunuzda

  • Etiket metnini ayarlama
  • sizeToFitEtiketinizi arayın

Kod Parçacığı:

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

benim durumumda ben 2 satır ayarlamak gerekiyordu dışında harika çalıştı - bir UITableViewCell içinde bir UILabel var ve 0 ayarı örtüşme yaptı, ama 2 ayar (hücre 2 satır için yeterli yer var)
eselk

34

Uyarlanabilir kullanıcı arayüzü için (iOS8 veya sonrası), UILabel'ın Dikey Hizalaması, noOfLines= 0` özellikleri değiştirilerek StoryBoard'dan ve

Kısıtlamalar

  1. UILabel LefMargin, RightMargin ve Üst Kenar Boşluğu Sınırlamalarını Ayarlama.

  2. Değişiklik Content Compression Resistance Priority For Vertical = 1000` Böylece Dikey> Yatay.

UILabel'in Kısıtlamaları

Düzenlendi:

noOfLines=0

ve aşağıdaki kısıtlamalar istenen sonuçları elde etmek için yeterlidir.

resim açıklamasını buraya girin


Bu harikaydı. Dikey> Yatay işini neden yaptığını açıklayabilir misiniz? Teşekkürler.
Gabriel

33

Bir UILabel alt sınıfı oluşturun. Tıkır tıkır çalışıyor:

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

As tartışılan burada .


27

Bu amaca ulaşmak için bir fonksiyon yazdım. Bir göz atabilirsiniz:

// çok satırlı bir etiketin yüksekliğini dikey ile üste hizalanacak şekilde ayarlayın
+ (geçersiz) alignLabelWithTop: (UILabel *) label {
  CGSize maxSize = CGSizeMake (label.frame.size.width, 999);
  label.adjustsFontSizeToFitWidth = HAYIR;

  // gerçek yüksekliği al
  CGSize actualSize = [label.text sizeWithFont: label.font constrainedToSize: maxSize lineBreakMode: label.lineBreakMode];
  CGRect rect = label.frame;
  rect.size.height = actualSize.height;
  label.frame = rect;
}

.Nasıl kullanılır? (LblHello, Arayüz oluşturucu tarafından oluşturulduysa, bazı UILabel özniteliklerini atlarım)

lblHello.text = @ "Merhaba Dünya! Merhaba Dünya! Merhaba Dünya! Merhaba Dünya! Merhaba Dünya! Merhaba Dünya! Merhaba Dünya! Merhaba Dünya!";
lblHello.numberOfLines = 5;
[Utils alignLabelWithTop: lblHello];

Blogumda da bir makale olarak yazdım: http://fstoke.me/blog/?p=2819


27

Tanıtılan sayfada kodun yanı sıra kodu okumak için biraz zaman aldım ve hepsinin etiketin kare boyutunu değiştirmeye çalıştıklarını, böylece varsayılan merkez dikey hizalamanın görünmeyeceğini gördüm.

Bununla birlikte, bazı durumlarda, etiketin çok fazla metni olsa bile etiketin tüm bu boşlukları işgal etmesini isteriz (örneğin, eşit yükseklikte birden çok satır).

Burada, etiketin sonuna sadece satır satırları pad ile çözmek için alternatif bir yol kullandım (pls aslında miras olduğunu unutmayın UILabel, ancak gerekli değildir):

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

Uygulamamda UILabel'ın line özelliğini 0 olarak ayarlamak ve UILabel'ın alt sınırını oluşturmak ve aşağıdaki resimde gösterildiği gibi> = 0 olarak ayarlandığından emin olmaktı. resim açıklamasını buraya girin


19

Buradaki önerileri aldım ve bir UILabel'i saran ve boyutlandıracak ve satır sayısını üste hizalanacak şekilde ayarlayacak bir görünüm oluşturdum. Bir alt görünüm olarak bir UILabel yazın:

@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 kullanabilirsiniz , dikey hizalamayı destekler.

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

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

16

Bu sorunun cevabının şimdi biraz güncel olmadığını buldum, bu yüzden bunu otomatik düzen hayranları için ekledik.

Otomatik düzen, bu sorunu oldukça önemsiz kılar. Etiketi eklediğimizi varsayarsak UIView *view, aşağıdaki kod bunu başaracaktır:

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

Etiketin yüksekliği otomatik olarak (kullanılarak intrinsicContentSize) hesaplanır ve etiket kenardan kenara yatay olarak, üst kısmına yerleştirilir view.


15

Yukarıdaki yöntemlerin çoğunu kullandım ve kullandığım hızlı ve kirli bir yaklaşımı eklemek istiyorum:

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

Dizedeki yeni satır sayısının herhangi bir metnin kullanılabilir dikey alanı doldurmasına neden olacağından emin olun ve UILabel'i taşan metni kesecek şekilde ayarlayın.

Çünkü bazen yeterince iyi, yeterince iyi .


Bununla birlikte, iOS cihazlarının ekran boyutu yüksekliklerindeki varyasyon göz önüne alındığında, uilabel değiştirme yüksekliğini (bir olasılıktır) hesaba katmak için değişken bir sayıda '\ n' olması gerekir. Dolayısıyla bu yöntem uzun vadede özellikle yararlı değildir.
Rambatino

Not: "hızlı ve kirli" değil "her zaman için mükemmel bir çözüm." Sadece söylüyorum'.
Code Roadie

2
@CodeRoadie Hızlı ve kirli benim için hile yaptı, gerçek cevap ile birkaç düzen sorunları vardı. Ben bunu "doğru yol" yapabilirdim, ama bana biraz zaman kazandığınız için teşekkür ederim!
Séraphin Hochart

@ dGambit lütfen dikkat: "hızlı ve kirli "
Code Roadie

1
Son zamanlarda görünümleri dinamik olarak yüklerken öğrendiğim bir şey UITextView, dlopenmetin girişini desteklemek için arayabiliyor olması. Bu, UI iş parçacığında büyük gecikmeye neden olabilir, bu nedenle bu yaklaşım çok daha performanslıdır!
yano

14

Çok satırlı, minimum yazı tipi boyutuna sahip olabilen ve ana görünümünde hem yatay hem de dikey olarak ortalanmış bir etikete sahip olmak istedim. Etiketimi programlı olarak görünümüme ekledim:

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

Ve sonra etiketimin metnini değiştirmek istediğimde ...

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

Umarım birine yardım eder!


14

(Github) FXLabel ayarlayarak kutunun bu out yapar label.contentModeiçin UIViewContentModeTop. Bu bileşen benim tarafımdan yapılmadı, ancak sık kullandığım ve tonlarca özelliğe sahip bir bileşen ve iyi çalışıyor gibi görünüyor.


11

etiketinizdeki metin dikey olarak ortalanmadığından bunu okuyan herkes için, bazı yazı tipi türlerinin eşit şekilde tasarlanmadığını unutmayın. örneğin, zapfino boyutu 16 olan bir etiket oluşturursanız, metnin dikey olarak tam olarak ortalanmadığını görürsünüz.

ancak helvetica ile çalışmak metninizi dikey olarak ortalar.


Birçok özel yazı tipi dikey ortaya hizalanmamıştır. UILabel veya UITextView her zaman dikey olarak ortalanmıştır. İOS programlamasında dikey hizalamayı düşünmeye gerek yoktur.
Amit Singh

11

Kullanın textRect(forBounds:limitedToNumberOfLines:).

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

Siz bir tanrısınız!
Diogo Antunes

1
Bu cevap almalısınız yolu o kadar güzel ve en temiz çözüm tarafından olduğu gibi daha upvotes!
Michael Ochs

10

UILabel alt sınıfını çizin ve çizim dikdörtgenini şu şekilde kısıtlayın:

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

    [super drawTextInRect:rect];
}

Newline dolgusu içeren çözümü denedim ve bazı durumlarda yanlış davranışlarla karşılaştım. Deneyimlerime göre, çizim rektini yukarıdaki gibi kısıtlamak karışıklıktan daha kolaydır numberOfLines.

PS UIViewContentMode'u bu şekilde kolayca desteklemeyi hayal edebilirsiniz:

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

Otomatik yerleşim kullanıyorsanız, kod veya IB'de dikey contentHuggingPriority değerini 1000 olarak ayarlayın. IB'de yükseklik önceliğini 1 olarak ayarlayıp ardından silerek yükseklik sınırlamasını kaldırmanız gerekebilir.


8

Karmaşık bir iş yapmadığınız sürece, UITextViewyerine kullanabilirsiniz UILabels.

Kaydırma işlevini devre dışı bırakın.

Metnin tamamen görüntülenmesini istiyorsanız sadece kullanıcı sizeToFitve sizeThatFits:yöntemler


8

Hızlıca,

let myLabel : UILabel!

Etiket metninizi ekrana sığdırmak için üstte

myLabel.sizeToFit()

Etiket yazı tipinizi ekran genişliğine veya belirli genişlik boyutuna sığdırmak için.

myLabel.adjustsFontSizeToFitWidth = YES

ve bazı metin etiketi için hizalama:

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified


Eski sözdiziminden sadece bir sözdizimi değişikliği [myLabel sizeToFit]. Teşekkür ederim!
velkoon

7

Bu eski bir çözüm, iOS'ta otomatik yerleşimi kullanın> = 6

Çözümüm:

  1. Satırları kendim ayır (etiket sarma ayarlarını yok sayılıyor)
  2. Kendi başıma çizgiler çizme (etiket hizalamasını yoksayarak)

@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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.