UILabel'in yazı tipi boyutunu dinamik olarak değiştirme


188

Şu anda bir UILabel:

factLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 280, 100)];
factLabel.text = @"some text some text some text some text";
factLabel.backgroundColor = [UIColor clearColor];
factLabel.lineBreakMode = UILineBreakModeWordWrap;
factLabel.numberOfLines = 10;
[self.view addSubview:factLabel];

İOS uygulamamın ömrü boyunca factLabelbir dizi farklı değer alır. Bazıları birden fazla cümleyle, diğerleri sadece 5 veya 6 kelimeyle.

UILabelYazı tipi boyutu değişecek ve metnin her zaman tanımladığım sınırlara sığacak şekilde nasıl ayarlayabilirim ?


2
2016 için, tek iyi çözümün "otomatik kullanım" yaklaşımını kullanmak olduğuna gerçekten inanıyorum . UILabel kutusunu istediğiniz gerçek boyutta yapın, yazı tipinin UILabel'i doldurmasını sağlayın, autoshrink'i seçin, büyük bir yazı tipi boyutu (300) ayarlayın ve en küçük / en büyük simülatörleri test ettiğinizden emin olun. (Yani, şu anda 4s / PadPro.) Tam açıklama: stackoverflow.com/a/35154493/294884 Bu, bugün tek gerçek çözümdür.
Fattie

Yanıtlar:


370

Tek satır:

factLabel.numberOfLines = 1;
factLabel.minimumFontSize = 8;
factLabel.adjustsFontSizeToFitWidth = YES;

Yukarıdaki kod, 8metninizi etiketin içine sığdırmaya çalışarak metninizin yazı tipi boyutunu aşağı (örneğin) ayarlayacaktır . numberOfLines = 1zorunludur.

Birden çok satır:

For numberOfLines > 1yoluyla nihai metnin boyutunu anlamaya bir yöntem yoktur NSString en sizeWithFont: ... UIKit ekleme yöntemleri, örneğin:

CGSize lLabelSize = [yourText sizeWithFont:factLabel.font
                                  forWidth:factLabel.frame.size.width
                             lineBreakMode:factLabel.lineBreakMode];

Bundan sonra, sonuçta kullanarak etiketinizi yeniden boyutlandırabilirsiniz lLabelSize, örneğin (yalnızca etiketin yüksekliğini değiştireceğinizi varsayarak):

factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, factLabel.frame.size.width, lLabelSize.height);

iOS6

Tek satır:

İOS6'dan başlayarak minimumFontSizekullanımdan kaldırıldı. Çizgi

factLabel.minimumFontSize = 8.;

şu şekilde değiştirilebilir:

factLabel.minimumScaleFactor = 8./factLabel.font.pointSize;

iOS7

Birden çok satır:

İOS7 ile başlayarak sizeWithFontkullanımdan kaldırılıyor. Çok satırlı kasa aşağıdakilere indirgenir:

factLabel.numberOfLines = 0;
factLabel.lineBreakMode = NSLineBreakByWordWrapping;
CGSize maximumLabelSize = CGSizeMake(factLabel.frame.size.width, CGFLOAT_MAX);
CGSize expectSize = [factLabel sizeThatFits:maximumLabelSize];
factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, expectSize.width, expectSize.height);

iOS 13 (Swift 5):

label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5

ancak bu metni tek bir satıra yerleştirir. ve factLabel.numberOfLines öğesini değiştirirsem yazı tipi boyutu dinamik olarak değişmez.
CodeGuy

@ reising1: haklısın. Bu, sizin için yeniden boyutlandırma çalışması yapmak için nasıl bir çerçeve oluşturacağınızdır.
Martin Babacaev

öyleyse sorumun cevabı, sağlanan çerçeveyi kullanarak bunu yapmanın bir yolu olmadığı mı?
CodeGuy

1
@ reising1: Bu durumda NSString UIKit ilavesi yöntemini de kullanabilirsiniz: sizeWithFont:constrainedToSize:lineBreakMode:Ama bu şekilde biraz zor
Martin Babacaev

6
İOS6'dan beri kullanımdan kaldırıldı. YerinemyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Norbert

72

minimumFontSizeiOS 6 ile kullanımdan kaldırıldı minimumScaleFactor.

yourLabel.adjustsFontSizeToFitWidth=YES;
yourLabel.minimumScaleFactor=0.5;

Bu, yazı tipi boyutunuzu etiket ve metnin genişliğine göre halleder.


Genellikle 0,8 kullanıyorum, çünkü 0,7 bile çok küçük gözüküyor. Tabii ki bazı metinler minimum ölçek faktörü 0.8 ile uyuşmayabilir, neyin daha iyi göründüğüne ve neyin okunamaz hale geleceğine karar verme meselesidir. OTOH uygulamalarım çok yardımcı olacak şekilde döndürülebilir.
gnasher729

adjustsFontSizeToFitWidthmetni yalnızca kaba sığmazsa azaltır
user25

24

@Eyal Ben Dov'un cevabına dayanarak, başka bir uygulamalarınızda kullanımını esnek hale getirmek için bir kategori oluşturmak isteyebilirsiniz.

Obs .: iOS 7 ile uyumlu hale getirmek için kodunu güncelledim

-Başlık dosyası

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

-Uygulama dosyası

#import "UILabel+DynamicFontSize.h"

@implementation UILabel (DynamicFontSize)

#define CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE 35
#define CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE 3

-(void) adjustFontSizeToFillItsContents
{
    NSString* text = self.text;

    for (int i = CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE; i>CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE; i--) {

        UIFont *font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
        NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];

        CGRect rectSize = [attributedText boundingRectWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil];

        if (rectSize.size.height <= self.frame.size.height) {
            self.font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
            break;
        }
    }

}

@end

-Kullanım

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Şerefe


benim için çalışmıyor. UILabel'ımın içeriği şimdi kesildi.
Adrian

1
Sizin için çalışmıyorsa, büyük olasılıkla etiketin çerçevesi henüz ayarlanmamıştır. Bunu çağırmadan önce çerçeveyi ayarlamayı deneyin (veya AutoLayout'u kullanıyorsanız setNeedsLayout/ öğesini arayın layoutIfNeeded).
bmueller

Aşağıdaki kilitlenme "'NSInvalidArgumentException', neden verir: 'NSConcreteAttributedString initWithString :: nil değeri'"
Mohamed Saleh

Bu, NSString'inizin sıfır olamayacağı anlamına gelir. Yazı tipi boyutunu UILabel'ın içeriğini dolduracak şekilde ayarlamak istiyorsanız, en azından bir metin sağladığınızı varsayıyorum.
Paulo Miguel Almeida

Bunun bir dezavantajı var. Karakterler arasındaki satır sonu, böylece kelimelerin farklı satırlara ayrıldığını görüyorsunuz. Bunu atlatmanın bir yolu var mı?
Özgür

24

Tek satır - İki yol vardır, basitçe değiştirebilirsiniz.

1- Pragmatik (Swift 3)

Sadece aşağıdaki kodu ekleyin

    yourLabel.numberOfLines = 1;
    yourLabel.minimumScaleFactor = 0.7;
    yourLabel.adjustsFontSizeToFitWidth = true;

2 - UILabel Nitelikleri denetçisini kullanma

i- Select your label- Set number of lines 1.
ii- Autoshrink-  Select Minimum Font Scale from drop down
iii- Set Minimum Font Scale value as you wish , I have set 0.7 as in below image. (default is 0.5)

resim açıklamasını buraya girin


22

2015. Birden fazla satırla çalışabilmesi için Swift ile iOS ve XCode'un en son sürümü için nasıl yapılacağını açıklayacak bir blog yazısı bulmam gerekiyordu.

  1. “Autoshrink” i “Minimum yazı tipi boyutu” olarak ayarlayın.
  2. yazı tipini istenen en büyük yazı tipi boyutuna ayarlayın (20 tane seçtim)
  3. “Satır Kesmelerini” “Sözcük Sarma” yerine “Kuyruğu Kes” olarak değiştirin.

Kaynak: http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/


Bu gerçekten bir hayat kurtarıcı !!
bhakti123

2
Süper serin .. Bu kesik kuyruk noktası en önemlisidir .. Kelime kaydırma otomatik yerleşimi durumunda Coz, yazı tipi boyutunu küçültme dürtüsü hissetmezken, kesik kuyruk otomatik yerleşimi metni bıçaktan kaydetmek zorunda kalır ve o zaman yazı tipini yeniden boyutlandırır.
GKK

12

Hızlı sürüm:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

Teşekkürler .. Görünüşe göre burada sıralama da önemlidir
Pramod

7

İşte UILabel için bir Swift uzantısı. Etiketin sınırlarının genişliğini ve yüksekliğini temel alarak yazı tipini yeniden boyutlandırmak için bir ikili arama algoritması çalıştırır. İOS 9 ve otomatik yerleşim ile çalıştığı test edildi.

KULLANIM:<label> Yazı tipi yeniden boyutlandırması gereken önceden tanımlı UILabel'niz nerede

<label>.fitFontForSize()

Varsayılan olarak, bu işlev 5pt ve 300pt yazı tipi boyutları arasında arama yapar ve yazı tipini metnini sınırlar içinde "mükemmel" olacak şekilde ayarlar (1.0pt içinde doğrudur). Parametreleri, örneğin 1pt ile etiketin geçerli yazı tipi boyutu arasında arama yapacak şekilde tanımlayabilirsiniz. içinde doğru bir şekilde aşağıdaki şekilde arama yapacak şekilde tanımlayabilirsiniz :

<label>.fitFontForSize(1.0, maxFontSize: <label>.font.pointSize, accuracy:0.1)

Aşağıdaki kodu dosyanıza kopyalayın / yapıştırın

extension UILabel {

    func fitFontForSize(var minFontSize : CGFloat = 5.0, var maxFontSize : CGFloat = 300.0, accuracy : CGFloat = 1.0) {
        assert(maxFontSize > minFontSize)
        layoutIfNeeded() // Can be removed at your own discretion
        let constrainedSize = bounds.size
        while maxFontSize - minFontSize > accuracy {
            let midFontSize : CGFloat = ((minFontSize + maxFontSize) / 2)
            font = font.fontWithSize(midFontSize)
            sizeToFit()
            let checkSize : CGSize = bounds.size
            if  checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width {
                minFontSize = midFontSize
            } else {
                maxFontSize = midFontSize
            }
        }
        font = font.fontWithSize(minFontSize)
        sizeToFit()
        layoutIfNeeded() // Can be removed at your own discretion
    }

}

NOT:layoutIfNeeded() Çağrıların her biri kendi takdirinize bağlı olarak kaldırılabilir


Ah - ama gerçekten otomatik yerleşim ile çalışmaz; "sizeToFit" ler bu durumda hiçbir şey yapmazlar.
Şişko

4

Biraz sofistike değil, ancak bu işe yaramalı, örneğin uilabelinizi 120x120'ye, maksimum yazı tipi boyutu 28 ile sınırlamak istediğinizi varsayalım:

magicLabel.numberOfLines = 0;
magicLabel.lineBreakMode = NSLineBreakByWordWrapping;
...
magicLabel.text = text;
    for (int i = 28; i>3; i--) {
        CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:(CGFloat)i] constrainedToSize:CGSizeMake(120.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
        if (size.height < 120) {
            magicLabel.font = [UIFont systemFontOfSize:(CGFloat)i];
            break;
        }
    }

Bu oldukça verimsiz görünüyor - UILabel'in sağlanan bazı boş alanlara sığması için kendisini dinamik olarak yükseltmesine izin vermelisiniz. Bunu tablo görünümü hücresinin başlık yazı tipi hesaplaması gibi bir şey için çalıştırırsanız, büyük gecikme sorunları alırsınız. Yaklaşım işe yarayabilir, ancak kesinlikle tavsiye edilmez.
Zorayr

Soruyu cevaplayan tek kişi olduğu için oy verin.
Jai

2

SizeToFit mesajını UITextView öğesine göndermeniz yeterlidir. Sadece metnine uyacak şekilde kendi yüksekliğini ayarlayacaktır. Kendi genişliğini veya kaynağını değiştirmez.

[textViewA1 sizeToFit];

Metne uyan boyut kabın alanı için çok büyük olduğunda ne olur? Örneğin, çağrıldıktan sonra, metin görünüme uygun şekilde kullanılabilir 100 puan var diyelim sizeToFitsenin textViewA1kırpılmış alma biter 200 puan olur.
Zorayr

0

Swift 2.0 Sürümü:

private func adapteSizeLabel(label: UILabel, sizeMax: CGFloat) {
     label.numberOfLines = 0
     label.lineBreakMode = NSLineBreakMode.ByWordWrapping
     let maximumLabelSize = CGSizeMake(label.frame.size.width, sizeMax);
     let expectSize = label.sizeThatFits(maximumLabelSize)
     label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, expectSize.width, expectSize.height)
}

0

Bu çözüm çok satırlı için çalışır:

Birkaç makaleyi takip ettikten ve metni otomatik olarak ölçekleyecek ve satır sayısını verilen etiket boyutuna en uygun şekilde ayarlayacak bir işlev gerektirdikten sonra kendim bir işlev yazdım. (örneğin, kısa bir dize bir satıra iyi sığacak ve büyük miktarda etiket çerçevesi kullanacak, oysa uzun bir güç otomatik olarak 2 veya 3 satıra bölünecek ve boyutu buna göre ayarlayacaktır)

Yeniden kullanmaktan çekinmeyin ve gerektiği gibi değiştirin. viewDidLayoutSubviewsİlk etiket çerçevesinin ayarlanması için işlemi bitirdikten sonra aradığınızdan emin olun .

+ (void)setFontForLabel:(UILabel *)label withMaximumFontSize:(float)maxFontSize andMaximumLines:(int)maxLines {
    int numLines = 1;
    float fontSize = maxFontSize;
    CGSize textSize; // The size of the text
    CGSize frameSize; // The size of the frame of the label
    CGSize unrestrictedFrameSize; // The size the text would be if it were not restricted by the label height
    CGRect originalLabelFrame = label.frame;

    frameSize = label.frame.size;
    textSize = [label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize: fontSize]}];

    // Work out the number of lines that will need to fit the text in snug
    while (((textSize.width / numLines) / (textSize.height * numLines) > frameSize.width / frameSize.height) && (numLines < maxLines)) {
        numLines++;
    }

    label.numberOfLines = numLines;

    // Get the current text size
    label.font = [UIFont systemFontOfSize:fontSize];
    textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
                                        options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                     attributes:@{NSFontAttributeName : label.font}
                                        context:nil].size;

    // Adjust the frame size so that it can fit text on more lines
    // so that we do not end up with truncated text
    label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, label.frame.size.width, label.frame.size.width);

    // Get the size of the text as it would fit into the extended label size
    unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;

    // Keep reducing the font size until it fits
    while (textSize.width > unrestrictedFrameSize.width || textSize.height > frameSize.height) {
        fontSize--;
        label.font = [UIFont systemFontOfSize:fontSize];
        textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
                                            options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                         attributes:@{NSFontAttributeName : label.font}
                                            context:nil].size;
        unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;
    }

    // Set the label frame size back to original
    label.frame = originalLabelFrame;
}

0

Animasyonlu yazı tipi boyutu değişikliğini uygulayan bir UILabel alt sınıfının dolgu kodu:

@interface SNTextLayer : CATextLayer

@end

@implementation SNTextLayer

- (void)drawInContext:(CGContextRef)ctx {
    // We override this to make text appear at the same vertical positon as in UILabel
    // (otherwise it's shifted tdown)
    CGFloat height = self.bounds.size.height;
    float fontSize = self.fontSize;
    // May need to adjust this somewhat if it's not aligned perfectly in your implementation
    float yDiff = (height-fontSize)/2 - fontSize/10;

    CGContextSaveGState(ctx);
    CGContextTranslateCTM(ctx, 0.0, yDiff);
    [super drawInContext:ctx];
     CGContextRestoreGState(ctx);
}

@end

@interface SNAnimatableLabel ()

@property CATextLayer* textLayer;

@end

@interface SNAnimatableLabel : UILabel

- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration;

@end



@implementation SNAnimatableLabel


- (void)awakeFromNib {
    [super awakeFromNib];
    _textLayer = [SNTextLayer new];
    _textLayer.backgroundColor = self.backgroundColor.CGColor;
    _textLayer.foregroundColor = self.textColor.CGColor;
    _textLayer.font = CGFontCreateWithFontName((CFStringRef)self.font.fontName);
    _textLayer.frame = self.bounds;
    _textLayer.string = self.text;
    _textLayer.fontSize = self.font.pointSize;
    _textLayer.contentsScale = [UIScreen mainScreen].scale;
    [_textLayer setPosition: CGPointMake(CGRectGetMidX(_textLayer.frame), CGRectGetMidY(_textLayer.frame))];
    [_textLayer setAnchorPoint: CGPointMake(0.5, 0.5)];
    [_textLayer setAlignmentMode: kCAAlignmentCenter];
    self.textColor = self.backgroundColor;
    // Blend text with background, so that it doens't interfere with textlayer text
    [self.layer addSublayer:_textLayer];
    self.layer.masksToBounds = NO;
}

- (void)setText:(NSString *)text {
    _textLayer.string = text;
    super.text = text;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    // Need to enlarge the frame, otherwise the text may get clipped for bigger font sizes
    _textLayer.frame = CGRectInset(self.bounds, -5, -5);
}

- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration {
    [CATransaction begin];
    [CATransaction setAnimationDuration:duration];
    _textLayer.fontSize = fontSize;
    [CATransaction commit];
}
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.