Tự động thay đổi kích thước phông chữ của UILabel


188

Tôi hiện có một 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];

Trong suốt vòng đời của ứng dụng iOS của tôi, factLabelnhận được một loạt các giá trị khác nhau. Một số có nhiều câu, số khác chỉ có 5 hoặc 6 từ.

Làm cách nào tôi có thể thiết lập UILabelđể kích thước phông chữ thay đổi để văn bản luôn khớp với giới hạn tôi đã xác định?


2
Trong năm 2016, tôi thực sự tin rằng giải pháp tốt duy nhất là sử dụng phương pháp "sử dụng tự động thu nhỏ". Đặt hộp UILabel có kích thước thực tế bạn muốn, làm cho phông chữ điền vào UILabel, chọn tự động thu nhỏ, đặt kích thước phông chữ khổng lồ (300) và chắc chắn kiểm tra các trình giả lập nhỏ nhất / lớn nhất. (Vì vậy, 4s / PadPro hiện tại.) Giải thích đầy đủ: stackoverflow.com/a/35154493/294884 Đây là giải pháp thực sự duy nhất hiện nay.
Fattie

Câu trả lời:


370

Dòng đơn:

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

Đoạn mã trên sẽ điều chỉnh kích thước phông chữ của văn bản của bạn xuống (ví dụ) 8cố gắng khớp văn bản của bạn trong nhãn. numberOfLines = 1là bắt buộc.

Nhiều dòng:

Để numberOfLines > 1có một phương pháp để tìm ra kích thước của văn bản cuối cùng thông qua size của NSStringWithFont: ... Các phương thức bổ sung UIKit , ví dụ:

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

Sau đó, bạn chỉ có thể thay đổi kích thước nhãn của mình bằng kết quả lLabelSize, ví dụ (giả sử rằng bạn sẽ chỉ thay đổi chiều cao của nhãn):

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

hệ điều hanh 6

Dòng đơn:

Bắt đầu với iOS6, minimumFontSizeđã không được chấp nhận. Dòng

factLabel.minimumFontSize = 8.;

có thể thay đổi thành:

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

IOS 7

Nhiều dòng:

Bắt đầu với iOS7, sizeWithFonttrở nên không dùng nữa. Trường hợp đa dòng được giảm xuống:

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

nhưng điều này đặt tất cả các văn bản trên một dòng. và nếu tôi thay đổi factLabel.numberOfLines, thì kích thước phông chữ không thay đổi linh hoạt.
CodeGuy

@ reising1: bạn nói đúng. Đây chỉ là cách tạo khung để thực hiện thay đổi kích thước công việc cho bạn.
Martin Babacaev

Vì vậy, câu trả lời cho câu hỏi của tôi là không có cách nào để làm điều đó bằng cách sử dụng khuôn khổ được cung cấp?
CodeGuy

1
@ reising1: Trong trường hợp này, bạn cũng có thể sử dụng phương pháp bổ sung NSString UIKit: sizeWithFont:constrainedToSize:lineBreakMode:Nhưng cách này hơi khó khăn một chút
Martin Babacaev

6
Nó không dùng nữa kể từ iOS6. Thay thế nó bằngmyLabel.minimumScaleFactor:10.0/[UIFont labelFontSize];
Norbert

72

minimumFontSizeđã bị phản đối với iOS 6. Bạn có thể sử dụng minimumScaleFactor.

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

Điều này sẽ chăm sóc kích thước phông chữ của bạn theo chiều rộng của nhãn và văn bản.


Tôi thường sử dụng 0,8, vì thậm chí 0,7 có xu hướng trông quá nhỏ. Tất nhiên một số văn bản có thể không phù hợp với hệ số tỷ lệ tối thiểu 0,8, đó là vấn đề quyết định cái gì trông đẹp hơn và nơi mọi thứ không thể đọc được. OTOH các ứng dụng của tôi có thể được xoay vòng giúp ích rất nhiều.
gnasher729

adjustsFontSizeToFitWidthchỉ giảm văn bản nếu nó không vừa trong container
user25

24

Dựa trên câu trả lời của @Eyal Ben Dov, bạn có thể muốn tạo một danh mục để làm cho nó linh hoạt để sử dụng trong các ứng dụng khác của bạn.

Quan sát: Tôi đã cập nhật mã của anh ấy để tương thích với iOS 7

-Tập tin tiêu đề

#import <UIKit/UIKit.h>

@interface UILabel (DynamicFontSize)

-(void) adjustFontSizeToFillItsContents;

@end

Tập tin thực hiện

#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

-Sử dụng

#import "UILabel+DynamicFontSize.h"

[myUILabel adjustFontSizeToFillItsContents];

Chúc mừng


nó không làm việc cho tôi Nội dung của UILabel của tôi bị cắt ngay bây giờ.
Adrian

1
Nếu nó không hoạt động với bạn, có lẽ vì khung của nhãn chưa được đặt. Hãy thử đặt khung trước khi gọi này (hoặc gọi setNeedsLayout/ layoutIfNeedednếu bạn đang sử dụng AutoLayout).
bmueller

Nó đưa ra sự cố sau "'NSInvalidArgumentException', lý do: 'NSConcittleAttributionString initWithString :: nil value'"
Mohamed Saleh

Điều đó có nghĩa là NSString của bạn không thể là con số không. Tôi giả sử rằng nếu bạn muốn điều chỉnh kích thước phông chữ để điền vào nội dung của UILabel, bạn ít nhất phải cung cấp một văn bản.
Paulo Miguel Almeida

Điều này có một nhược điểm. Nó ngắt dòng giữa các ký tự, vì vậy bạn thấy các từ được chia thành các dòng khác nhau. Có cách nào để phá vỡ điều này?
Özgür

23

Dòng đơn - Có hai cách, bạn chỉ cần thay đổi.

1- Thực dụng (Swift 3)

Chỉ cần thêm mã sau đây

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

2 - Sử dụng thanh tra thuộc tính 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)

nhập mô tả hình ảnh ở đây


22

Đó là năm 2015. Tôi đã phải đi tìm một bài đăng trên blog để giải thích cách thực hiện nó cho phiên bản iOS và XCode mới nhất với Swift để nó hoạt động với nhiều dòng.

  1. thiết lập vùng Autoshrink tinh thành kích thước phông chữ tối thiểu.
  2. Đặt phông chữ thành kích thước phông chữ mong muốn lớn nhất (Tôi đã chọn 20)
  3. Thay đổi dòng sản phẩm phá vỡ đường dây từ từ Word

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


Đây thực sự là một cứu tinh !!
bhakti123

2
Rất tuyệt vời .. Điểm đuôi cắt ngắn đó là quan trọng nhất .. Coz trong trường hợp tự động ngắt từ không bao giờ cảm thấy thôi thúc giảm kích thước phông chữ, trong khi đó, khi tự động cắt đuôi phải cắt văn bản khỏi lưỡi dao và nó sau đó nó thay đổi kích thước phông chữ.
GKK

12

Phiên bản Swift:

textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5

Cảm ơn .. Có vẻ như ở đây trình tự cũng trưởng thành
Pramod

7

Đây là một phần mở rộng Swift cho UILabel. Nó chạy một thuật toán tìm kiếm nhị phân để thay đổi kích thước phông chữ dựa trên chiều rộng và chiều cao của giới hạn của nhãn. Đã thử nghiệm để hoạt động với iOS 9 và tự động thanh toán.

SỬ DỤNG: Trường hợp <label>UILabel được xác định trước của bạn cần thay đổi kích thước phông chữ

<label>.fitFontForSize()

Theo mặc định, chức năng này tìm kiếm trong phạm vi kích thước phông chữ 5pt và 300pt và đặt phông chữ phù hợp với văn bản của nó "hoàn hảo" trong giới hạn (chính xác trong vòng 1pt). Bạn có thể xác định các tham số để nó, ví dụ, tìm kiếm giữa 1ptkích thước phông chữ hiện tại của nhãn chính xác trong vòng 0,1pts theo cách sau:

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

Sao chép / Dán đoạn mã sau vào tệp của bạ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
    }

}

LƯU Ý: Mỗi layoutIfNeeded()cuộc gọi có thể được loại bỏ theo ý của bạn


À - nhưng nó không thực sự hoạt động với autolayout; "sizeToFit" không làm gì trong trường hợp đó.
Fattie

4

Nó hơi phức tạp một chút nhưng điều này sẽ hoạt động, ví dụ: giả sử bạn muốn giới hạn uilabel của mình thành 120x120, với kích thước phông chữ tối đa là 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;
        }
    }

Điều này có vẻ không hiệu quả - bạn nên để cho UILabel tự động tăng chiều cao để phù hợp với một số không gian có sẵn được cung cấp. Nếu bạn chạy cái này cho một cái gì đó như tính toán phông chữ tiêu đề của ô xem bảng, bạn sẽ gặp các vấn đề lớn về độ trễ. Cách tiếp cận có thể làm việc, nhưng chắc chắn không được khuyến khích.
Zorayr

Up-vote vì là người duy nhất thực sự trả lời câu hỏi.
Jai

2

Chỉ cần gửi tin nhắn sizeToFit đến UITextView. Nó sẽ điều chỉnh chiều cao của chính nó để phù hợp với văn bản của nó. Nó sẽ không thay đổi chiều rộng hoặc nguồn gốc của chính nó.

[textViewA1 sizeToFit];

Điều gì xảy ra khi kích thước phù hợp với văn bản quá lớn so với không gian của người chứa? Ví dụ, giả sử bạn có 100 điểm có sẵn để phù hợp với độ xem văn bản, sau khi gọi sizeToFitbạn textViewA1trở thành 200 điểm mà kết thúc lên nhận cắt.
Zorayr

0

Phiên bản 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

Giải pháp này hoạt động cho multiline:

Sau khi theo dõi một số bài viết và yêu cầu một chức năng tự động chia tỷ lệ văn bản và điều chỉnh số lượng dòng để phù hợp nhất với kích thước nhãn đã cho, tôi đã tự mình viết một chức năng. (ví dụ: một chuỗi ngắn sẽ vừa vặn trên một dòng và sử dụng một lượng lớn khung nhãn, trong khi đó, một chuỗi dài sẽ tự động phân tách thành 2 hoặc 3 dòng và điều chỉnh kích thước phù hợp)

Hãy sử dụng lại và điều chỉnh theo yêu cầu. Hãy chắc chắn rằng bạn gọi nó sau khi viewDidLayoutSubviewskết thúc để khung nhãn ban đầu đã được đặt.

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

Dưới đây là mã điền của lớp con UILabel thực hiện thay đổi kích thước phông chữ hoạt hình:

@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];
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.