Mengubah ukuran font UILabel secara dinamis

188

Saat ini saya punya 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];

Sepanjang masa pakai aplikasi iOS saya, factLabeldapatkan banyak nilai yang berbeda. Beberapa dengan beberapa kalimat, yang lain hanya dengan 5 atau 6 kata.

Bagaimana saya bisa mengatur UILabelsehingga ukuran font berubah sehingga teks selalu sesuai dengan batas yang saya tentukan?

CodeGuy
sumber
2
Untuk 2016, saya benar-benar percaya satu-satunya solusi yang baik adalah dengan menggunakan pendekatan "gunakan autoshrinking". Jadikan kotak UILabel ukuran sebenarnya yang Anda inginkan, buat fonta mengisi UILabel, pilih autoshrink, atur tituler besar ukuran font (300), dan pastikan untuk menguji pada simulator terkecil / terbesar. (Jadi, 4s / PadPro saat ini.) Penjelasan lengkap: stackoverflow.com/a/35154493/294884 Ini adalah satu-satunya solusi nyata hari ini.
Fattie

Jawaban:

370

Garis tunggal:

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

Kode di atas akan menyesuaikan ukuran font teks Anda ke (misalnya) 8mencoba menyesuaikan teks Anda di dalam label. numberOfLines = 1wajib.

Beberapa baris:

Karena numberOfLines > 1ada metode untuk mengetahui ukuran teks akhir melalui ukuran NSStringWithFont: ... Metode penambahan UIKit , misalnya:

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

Setelah itu Anda dapat mengubah ukuran label menggunakan hasil lLabelSize, misalnya (dengan asumsi Anda hanya akan mengubah tinggi label):

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

iOS6

Garis tunggal:

Dimulai dengan iOS6, minimumFontSizetelah ditinggalkan. Garis

factLabel.minimumFontSize = 8.;

dapat diubah menjadi:

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

IOS 7

Beberapa baris:

Mulai dengan iOS7, sizeWithFontmenjadi usang. Kasing multiline dikurangi menjadi:

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
Martin Babacaev
sumber
tetapi ini menempatkan teks semua pada satu baris. dan jika saya mengubah factLabel.numberOfLines, maka ukuran font tidak berubah secara dinamis.
CodeGuy
@ reising1: Anda benar. Ini hanya bagaimana membuat kerangka kerja untuk melakukan pengubahan ukuran pekerjaan untuk Anda.
Martin Babacaev
jadi kemudian jawaban untuk pertanyaan saya adalah bahwa tidak ada cara untuk melakukannya menggunakan kerangka yang disediakan?
CodeGuy
1
@ reising1: Dalam hal ini Anda juga dapat menggunakan metode penambahan NSString UIKit: sizeWithFont:constrainedToSize:lineBreakMode:Tapi cara ini agak sulit
Martin Babacaev
6
Sudah usang sejak iOS6. Gantikan denganmyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Norbert
72

minimumFontSizetelah ditinggalkan dengan iOS 6. Anda dapat menggunakan minimumScaleFactor.

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

Ini akan menangani ukuran font Anda sesuai lebar label dan teks.

Amit Singh
sumber
Saya biasanya menggunakan 0,8, karena bahkan 0,7 cenderung terlihat terlalu kecil. Tentu saja beberapa teks mungkin tidak cocok dengan faktor skala minimum 0,8, itu masalah memutuskan apa yang terlihat lebih baik dan di mana segala sesuatu menjadi tidak dapat dibaca. OTOH aplikasi saya dapat diputar yang sangat membantu.
gnasher729
adjustsFontSizeToFitWidthhanya mengurangi teks jika tidak muat di dalam wadah
user25
24

Berdasarkan jawaban @Eyal Ben Dov Anda mungkin ingin membuat kategori agar fleksibel untuk digunakan dalam aplikasi Anda yang lain.

Obs .: Saya sudah memperbarui kodenya agar kompatibel dengan iOS 7

File -Header

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

File -Implementation

#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

-Pemakaian

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Bersulang

Paulo Miguel Almeida
sumber
itu tidak bekerja untuk saya. Konten UILabel saya terputus sekarang.
Adrian
1
Jika tidak berhasil untuk Anda, mungkin karena bingkai label belum ditetapkan. Coba atur bingkai sebelum memanggil ini (atau panggil setNeedsLayout/ layoutIfNeededjika Anda menggunakan AutoLayout).
bmueller
Ini memberikan crash berikut "'NSInvalidArgumentException', alasan: 'NSConcreteAttributedString initWithString :: nil value'"
Mohamed Saleh
Ini berarti NSString Anda tidak boleh nol. Saya berasumsi bahwa jika Anda ingin menyesuaikan ukuran font untuk mengisi konten UILabel, Anda setidaknya harus menyediakan teks.
Paulo Miguel Almeida
Ini memiliki kekurangan. Garis terpecah antar karakter, sehingga Anda melihat kata-kata terpecah menjadi garis yang berbeda. Apakah ada cara untuk menghindari ini?
Özgür
24

Baris tunggal - Ada dua cara, Anda bisa mengubahnya.

1- Secara pragmatis (Cepat 3)

Cukup tambahkan kode berikut

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

2 - Menggunakan inspektur Atribut UILabel

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)

masukkan deskripsi gambar di sini

Devendra Singh
sumber
22

Ini tahun 2015. Saya harus pergi untuk menemukan posting blog yang akan menjelaskan bagaimana melakukannya untuk versi terbaru iOS dan XCode dengan Swift sehingga dapat bekerja dengan beberapa baris.

  1. atur "Autoshrink" ke "Ukuran font minimum."
  2. mengatur font ke ukuran font yang diinginkan terbesar (saya memilih 20)
  3. Ubah "Line Breaks" dari "Word Wrap" menjadi "Truncate Tail."

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

zavtra
sumber
Ini benar-benar penyelamat !!
bhakti123
2
Sangat keren .. Titik potong truncate itu yang paling penting .. Karena jika kata wrap autolayout tidak merasakan keinginan untuk mengurangi ukuran font, sedangkan ketika truncate tail autolayout harus menyimpan teks dari blade dan itu kemudian mengubah ukuran font.
GKK
12

Versi cepat:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5
Esqarrouth
sumber
Terima kasih .. Sepertinya urutan di sini juga penting
Pramod
7

Ini ekstensi Swift untuk UILabel. Ini menjalankan algoritma pencarian biner untuk mengubah ukuran font berdasarkan lebar dan tinggi batas label. Diuji untuk bekerja dengan iOS 9 dan pembayaran otomatis.

PENGGUNAAN: Di mana <label>UILabel Anda yang telah ditentukan sebelumnya perlu diubah ukuran fontnya

<label>.fitFontForSize()

Secara default, fungsi ini mencari dalam kisaran ukuran font 5pt dan 300pt dan mengatur font agar sesuai dengan teksnya "dengan sempurna" dalam batas (akurat dalam 1.0pt). Anda dapat menetapkan parameter sehingga, misalnya, mencari antara 1pt dan ukuran font label saat ini secara akurat dalam 0,1 poin dengan cara berikut:

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

Salin / Tempel kode berikut ke dalam file Anda

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
    }

}

CATATAN: Setiap layoutIfNeeded()panggilan dapat dihapus atas kebijakan Anda sendiri

Avi Frankl
sumber
Ah - tetapi tidak benar-benar berfungsi dengan autolayout; "sizeToFit" tidak melakukan apa pun dalam kasus itu.
Fattie
4

Agak sedikit tidak canggih tetapi ini harus bekerja, misalnya katakanlah Anda ingin membatasi uilabel Anda ke 120x120, dengan ukuran font maksimal 28:

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;
        }
    }
Eyal Ben Dov
sumber
Ini tampaknya agak tidak efisien - Anda harus membiarkan UILabel secara dinamis tinggi agar sesuai dengan beberapa ruang yang tersedia. Jika Anda menjalankan ini untuk sesuatu seperti perhitungan font judul tampilan sel tabel, Anda akan mendapatkan masalah lagging utama. Pendekatannya mungkin berhasil, tetapi jelas tidak direkomendasikan.
Zorayr
Pilih untuk menjadi satu-satunya orang yang benar-benar menjawab pertanyaan.
Jai
2

Cukup kirim pesan sizeToFit ke UITextView. Ini akan menyesuaikan ketinggiannya sendiri agar pas dengan teksnya. Itu tidak akan mengubah lebar atau asal sendiri.

[textViewA1 sizeToFit];
codercat
sumber
Apa yang terjadi bila ukuran yang sesuai dengan teks terlalu besar untuk ruang penampung? Sebagai contoh, katakanlah Anda memiliki 100 poin yang tersedia agar sesuai dengan tampilan teks, setelah memanggil sizeToFitAnda textViewA1menjadi 200 poin yang akhirnya dipotong.
Zorayr
0

Versi Swift 2.0:

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)
}
Phil
sumber
0

Solusi ini berfungsi untuk multiline:

Setelah mengikuti beberapa artikel, dan membutuhkan fungsi yang secara otomatis akan menskala teks dan menyesuaikan jumlah baris agar sesuai dengan ukuran label yang diberikan, saya menulis sendiri fungsi. (mis. string pendek akan cocok dengan baik pada satu garis dan menggunakan sejumlah besar bingkai label, sedangkan kuat panjang akan secara otomatis terpecah menjadi 2 atau 3 garis dan menyesuaikan ukurannya)

Jangan ragu untuk menggunakannya kembali dan mengubah sesuai kebutuhan. Pastikan Anda memanggilnya setelah viewDidLayoutSubviewsselesai sehingga bingkai label awal telah ditetapkan.

+ (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;
}
aaroncatlin
sumber
0

Berikut adalah kode isian dari subkelas UILabel yang mengimplementasikan perubahan ukuran font animasi:

@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];
}
ahli video
sumber