การเปลี่ยนขนาดตัวอักษรของ UILabel แบบไดนามิก


188

ฉันมี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];

ตลอดชีวิตของแอปพลิเคชั่น iOS ของฉันfactLabelจะได้รับคุณค่าที่แตกต่างกันมากมาย บางประโยคมีหลายประโยคและบางประโยคมีเพียง 5 หรือ 6 คำ

ฉันจะตั้งค่าUILabelขนาดตัวอักษรให้เปลี่ยนแปลงได้อย่างไรเพื่อให้ข้อความมีขนาดพอดีกับขอบเขตที่ฉันกำหนดไว้เสมอ


2
สำหรับปี 2559 ฉันเชื่อว่าทางออกที่ดีเพียงอย่างเดียวคือใช้วิธี "ใช้ autoshrinking" ทำให้กล่อง UILabel มีขนาดตามจริงที่คุณต้องการสร้างแบบอักษรให้เต็ม UILabel เลือก autoshrink ตั้งค่าขนาดตัวอักษรขนาดใหญ่ที่มีขนาดใหญ่ (300) และให้แน่ใจว่าได้ทดสอบกับเครื่องจำลองที่เล็กที่สุด / ใหญ่ที่สุด (ดังนั้น 4s / PadPro ในปัจจุบัน) คำอธิบายแบบเต็ม: stackoverflow.com/a/35154493/294884 นี่เป็นทางออกที่แท้จริงเพียงวันนี้
Fattie

คำตอบ:


370

แถวเดียว:

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

โค้ดด้านบนจะปรับขนาดตัวอักษรของข้อความของคุณให้เป็น (ตัวอย่าง) 8พยายามปรับให้พอดีกับข้อความของคุณภายในฉลาก numberOfLines = 1เป็นสิ่งจำเป็น

หลายสาย:

สำหรับnumberOfLines > 1วิธีการหาขนาดของข้อความสุดท้ายผ่านขนาดของ NSStringWithFont: ...วิธีการเติม UIKitเช่น:

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

หลังจากนั้นคุณสามารถปรับขนาดป้ายกำกับของคุณโดยใช้ผลลัพธ์lLabelSizeเช่น (สมมติว่าคุณจะเปลี่ยนเฉพาะความสูงของป้ายกำกับ):

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

iOS6

แถวเดียว:

เริ่มต้นด้วย iOS6 minimumFontSizeแล้วเลิกใช้แล้ว เส้น

factLabel.minimumFontSize = 8.;

สามารถเปลี่ยนเป็น:

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

ไอโอเอส 7

หลายสาย:

เริ่มต้นด้วย iOS7 sizeWithFontเลิกใช้แล้ว กรณีหลายเส้นลดลงเป็น:

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

แต่สิ่งนี้ทำให้ข้อความทั้งหมดในบรรทัดเดียว และถ้าฉันเปลี่ยน factLabel.numberOfLines ขนาดตัวอักษรจะไม่เปลี่ยนแบบไดนามิก
CodeGuy

@ reising1: คุณพูดถูก นี่เป็นเพียงวิธีการทำกรอบการทำงานเพื่อปรับขนาดสำหรับคุณ
Martin Babacaev

ดังนั้นคำตอบสำหรับคำถามของฉันคือว่าไม่มีวิธีที่จะทำโดยใช้กรอบการทำงานที่ให้ไว้?
CodeGuy

1
@ reising1: ในกรณีนี้คุณยังสามารถใช้วิธีการของ NSString UIKit นอกจากนี้: sizeWithFont:constrainedToSize:lineBreakMode:แต่วิธีนี้ค่อนข้างยากนิดหน่อย
Martin Babacaev

6
เลิกใช้แล้วตั้งแต่ iOS6 แทนที่ด้วยmyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Norbert

72

minimumFontSizeเลิกใช้แล้วกับ iOS 6 คุณสามารถminimumScaleFactorใช้ได้

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

สิ่งนี้จะดูแลขนาดตัวอักษรของคุณตามความกว้างของฉลากและข้อความ


ฉันมักจะใช้ 0.8 เพราะแม้แต่ 0.7 มักจะดูเล็กเกินไป แน่นอนว่าข้อความบางอย่างอาจไม่พอดีกับปัจจัยขั้นต่ำสุด 0.8 มันเป็นเรื่องของการตัดสินใจว่าอะไรที่ดูดีกว่าและสิ่งที่อ่านไม่ออก OTOH แอปของฉันสามารถหมุนได้ซึ่งช่วยได้มาก
gnasher729

adjustsFontSizeToFitWidthลดเฉพาะข้อความหากไม่พอดีภายในคอนเทนเนอร์
user25

24

จากคำตอบของ @Eyal Ben Dov คุณอาจต้องการสร้างหมวดหมู่เพื่อให้ยืดหยุ่นในการใช้งานภายในแอพอื่นของคุณ

ข้อสังเกต: ฉันได้อัปเดตรหัสของเขาเพื่อให้เข้ากันได้กับ iOS 7

ไฟล์ส่วนหัว

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

ไฟล์การใช้งาน

#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

-Usage

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

ไชโย


มันไม่ทำงานสำหรับฉัน เนื้อหาของ UILabel ของฉันถูกตัดออกไปแล้ว
เอเดรียน

1
ถ้ามันไม่ทำงานสำหรับคุณอาจเป็นเพราะเฟรมของฉลากยังไม่ได้ตั้งค่า ลองตั้งค่าเฟรมก่อนเรียกสิ่งนี้ (หรือโทรsetNeedsLayout/ layoutIfNeededถ้าคุณใช้ AutoLayout)
bmueller

มันให้ความผิดพลาดต่อไปนี้ "'NSInvalidArgumentException' เหตุผล: 'NSConcreteAttributedString initWithString :: nil value'"
Mohamed Saleh

หมายความว่า NSString ของคุณไม่สามารถเป็นศูนย์ได้ ฉันสมมติว่าถ้าคุณต้องการปรับขนาดตัวอักษรเพื่อเติมเนื้อหาของ UILabel อย่างน้อยคุณก็จะมีข้อความ
เปาโลมิเกลอัลไมด้า

นี่คือข้อเสียเปรียบ มันแบ่งบรรทัดระหว่างตัวละครดังนั้นคุณจะเห็นคำที่แบ่งออกเป็นบรรทัดที่แตกต่างกัน มีวิธีหลีกเลี่ยงสิ่งนี้หรือไม่?
Özgür

24

บรรทัดเดียว - มีสองวิธีคุณสามารถเปลี่ยนได้

1- ในทางปฏิบัติ(รวดเร็ว 3)

เพียงเพิ่มรหัสต่อไปนี้

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

2 - การใช้ตัวตรวจสอบคุณสมบัติของ 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)

ป้อนคำอธิบายรูปภาพที่นี่


22

ปี 2558 ฉันต้องไปหาโพสต์บล็อกที่จะอธิบายวิธีการทำสำหรับ iOS เวอร์ชันล่าสุดและ XCode ด้วย Swift เพื่อให้สามารถทำงานกับหลาย ๆ สายได้

  1. ตั้งค่า "Autoshrink" เป็น "ขนาดตัวอักษรขั้นต่ำ"
  2. ตั้งค่าแบบอักษรเป็นขนาดแบบอักษรที่พึงประสงค์มากที่สุด (ฉันเลือก 20)
  3. เปลี่ยน“ ตัวแบ่งบรรทัด” จาก“ การตัดคำ” เป็น“ ตัดส่วนท้าย”

ที่มา: http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/


นี่คือการช่วยชีวิตอย่างแท้จริง !!
bhakti123

2
เด็ดสุด ๆ .. จุดตัดปลายนั้นสำคัญที่สุด .. เพราะในกรณีของการห่อคำอัตโนมัติไม่รู้สึกอยากที่จะลดขนาดตัวอักษรในขณะที่มันถูกตัดหางอัตโนมัติเพื่อบันทึกข้อความจากใบมีดและมัน คือว่ามันปรับขนาดตัวอักษร
GKK


7

นี่คือส่วนขยาย Swift สำหรับ UILabel มันรันอัลกอริธึมการค้นหาแบบไบนารีเพื่อปรับขนาดฟอนต์ตามความกว้างและความสูงของขอบเขตของฉลาก ผ่านการทดสอบการใช้งานกับ iOS 9 และการชำระอัตโนมัติ

การใช้งาน:<label> UILabel ที่กำหนดไว้ล่วงหน้าของคุณอยู่ที่ไหนที่ต้องปรับขนาดแบบอักษร

<label>.fitFontForSize()

ตามค่าเริ่มต้นฟังก์ชั่นนี้จะค้นหาภายในขนาดตัวอักษรขนาด 5 และ 300pt และตั้งค่าแบบอักษรให้พอดีกับข้อความ "สมบูรณ์แบบ" ภายในขอบเขต (ถูกต้องภายใน 1.0 พอยต์) คุณสามารถกำหนดพารามิเตอร์เพื่อให้ตัวอย่างเช่นค้นหาระหว่าง1ptและขนาดแบบอักษรปัจจุบันของป้ายกำกับอย่างถูกต้องภายใน0.1ptsด้วยวิธีต่อไปนี้:

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

คัดลอก / วางรหัสต่อไปนี้ลงในไฟล์ของคุณ

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
    }

}

หมายเหตุ:การlayoutIfNeeded()โทรแต่ละสายสามารถลบได้ตามดุลยพินิจของคุณเอง


อ่า - แต่มันใช้งานไม่ได้กับการชำระอัตโนมัติ "sizeToFit" ไม่ได้ทำอะไรในกรณีนี้
Fattie

4

มันค่อนข้างซับซ้อนเล็กน้อย แต่ควรใช้งานได้เช่นสมมติว่าคุณต้องการใส่ uilabel ของคุณเป็น 120x120 ด้วยขนาดตัวอักษรสูงสุด 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;
        }
    }

ดูเหมือนว่าจะค่อนข้างไม่มีประสิทธิภาพ - คุณควรปล่อยให้ UILabel มีความสูงแบบไดนามิกเพื่อให้พอดีกับพื้นที่ว่างที่มีให้ หากคุณเรียกใช้สิ่งนี้เช่นการคำนวณแบบอักษรชื่อของเซลล์มุมมองตารางคุณจะพบปัญหาการล้าหลังที่สำคัญ วิธีการนี้อาจใช้งานได้ แต่ไม่แนะนำอย่างแน่นอน
Zorayr

โหวตให้เป็นคนเดียวที่ตอบคำถามได้จริง
ใจ

2

เพียงส่งข้อความ sizeToFit ไปที่ UITextView มันจะปรับความสูงของตัวเองให้พอดีกับข้อความ มันจะไม่เปลี่ยนความกว้างหรือที่มาของมันเอง

[textViewA1 sizeToFit];

จะเกิดอะไรขึ้นเมื่อขนาดที่เหมาะกับข้อความมีขนาดใหญ่เกินไปสำหรับพื้นที่ของคอนเทนเนอร์ ตัวอย่างเช่นสมมติว่าคุณมี 100 คะแนนเพื่อให้พอดีกับมุมมองข้อความหลังจากการโทรsizeToFitของคุณtextViewA1กลายเป็น 200 คะแนนซึ่งจบลงด้วยการถูกครอบตัด
Zorayr

0

เวอร์ชั่น 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)
}

0

วิธีนี้ใช้ได้กับ multiline:

หลังจากติดตามบทความต่างๆและต้องการฟังก์ชั่นที่จะปรับขนาดข้อความโดยอัตโนมัติและปรับจำนวนบรรทัดให้พอดีกับขนาดฉลากที่กำหนดมากที่สุดฉันได้เขียนฟังก์ชั่นด้วยตัวเอง (เช่นสตริงสั้น ๆ น่าจะพอดีกับหนึ่งบรรทัดและใช้กรอบป้ายจำนวนมากในขณะที่ความยาวที่แข็งแกร่งจะแบ่งออกเป็น 2 หรือ 3 บรรทัดโดยอัตโนมัติและปรับขนาดให้เหมาะสม)

อย่าลังเลที่จะใช้ซ้ำและปรับแต่งตามที่ต้องการ ตรวจสอบให้แน่ใจว่าคุณเรียกมันว่าหลังจากviewDidLayoutSubviewsเสร็จสิ้นเพื่อให้มีการตั้งค่ากรอบป้ายกำกับเริ่มต้น

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

นี่คือรหัสการเติมของคลาสย่อย UILabel ที่ใช้การเปลี่ยนแปลงขนาดตัวอักษรแบบเคลื่อนไหว:

@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];
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.