Подтвердить что ты не робот

Многострочный UILabel с настройкамиFontSizeToFitWidth

У меня есть многострочный UILabel, размер шрифта которого я бы хотел настроить в зависимости от длины текста. Весь текст должен вставляться в рамку метки без его усечения.

К сожалению, согласно документации, свойство adjustsFontSizeToFitWidth действует только тогда, когда свойство numberOfLines установлено в 1 ".

Я попытался определить скорректированный размер шрифта, используя

-[NSString (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode]

а затем уменьшаем размер шрифта до его соответствия. К сожалению, этот метод внутренне усекает текст, чтобы он вписывался в указанный размер и возвращал размер результирующей усеченной строки.

4b9b3361

Ответ 1

В этот вопрос, 0x90 предоставляет решение, которое, хотя и немного уродливое, делает то, что я хочу. В частности, он правильно относится к ситуации, когда одно слово не соответствует ширине при первоначальном размере шрифта. Я немного изменил код, чтобы он работал как категория на NSString:

- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
    CGFloat fontSize = [font pointSize];
    CGFloat height = [self sizeWithFont:font constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    UIFont *newFont = font;

    //Reduce font size while too large, break if no height (empty string)
    while (height > size.height && height != 0) {   
        fontSize--;  
        newFont = [UIFont fontWithName:font.fontName size:fontSize];   
        height = [self sizeWithFont:newFont constrainedToSize:CGSizeMake(size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap].height;
    };

    // Loop through words in string and resize to fit
    for (NSString *word in [self componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
        CGFloat width = [word sizeWithFont:newFont].width;
        while (width > size.width && width != 0) {
            fontSize--;
            newFont = [UIFont fontWithName:font.fontName size:fontSize];   
            width = [word sizeWithFont:newFont].width;
        }
    }
    return fontSize;
}

Чтобы использовать его с помощью UILabel:

    CGFloat fontSize = [label.text fontSizeWithFont:[UIFont boldSystemFontOfSize:15] constrainedToSize:label.frame.size];
    label.font = [UIFont boldSystemFontOfSize:fontSize];

EDIT: Исправлен код для инициализации newFont с помощью font. Исправляет сбой при определенных обстоятельствах.

Ответ 2

Этот код, представленный в этом сообщении в блоге, также затрагивает эту проблему. Однако я обнаружил, что циклический выбор всех возможных размеров шрифта был слишком медленным, если вам нужно рассчитать размеры шрифтов для многих ярлыков одновременно, поэтому я немного изменил код, и теперь он работает намного быстрее.

Единственная проблема с моим решением, насколько мне известно, заключается в том, что он не учитывает ситуацию, когда одно слово не соответствует ширине при первоначальном размере шрифта, как указано Ortwin.

Итак, если вы ищете решение, которое работает быстрее, попробуйте это. (Он работает как категория на UIFont)

+ (UIFont*)fontWithName:(NSString *)fontName minSize:(CGFloat)minSize maxSize:(CGFloat)maxSize constrainedToSize:(CGSize)labelSize forText:(NSString*)text {

UIFont* font = [UIFont fontWithName:fontName size:maxSize];

CGSize constraintSize = CGSizeMake(labelSize.width, MAXFLOAT);
NSRange range = NSMakeRange(minSize, maxSize);

int fontSize = 0;
for (NSInteger i = maxSize; i > minSize; i--)
{
    fontSize = ceil(((float)range.length + (float)range.location) / 2.0);

    font = [font fontWithSize:fontSize];
    CGSize size = [text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];

    if (size.height <= labelSize.height) 
        range.location = fontSize;
    else 
        range.length = fontSize - 1;

    if (range.length == range.location) 
    {
        font = [font fontWithSize:range.location];
        break;
    }
}

return font;
}

Ответ 3

В некоторых случаях изменение "Разрывов строк" ​​от "Word Wrap" до "Truncate Tail" может быть всем, что вам нужно, если вы знаете, сколько строк вы хотите (например, "2" ): Кредит: Бекки Хансмайер

Ответ 4

Спасибо, с этим и немного больше от кого-то другого, я сделал этот пользовательский UILabel, который будет уважать минимальный размер шрифта и там есть бонусная опция для выравнивания текста.

ч:

@interface EPCLabel : UILabel {
    float originalPointSize;
    CGSize originalSize;
}

@property (nonatomic, readwrite) BOOL alignTextOnTop;
@end

т

#import "EPCLabel.h"

@implementation EPCLabel
@synthesize alignTextOnTop;

-(void)verticalAlignTop {
    CGSize maximumSize = originalSize;
    NSString *dateString = self.text;
    UIFont *dateFont = self.font;
    CGSize dateStringSize = [dateString sizeWithFont:dateFont 
                                   constrainedToSize:CGSizeMake(self.frame.size.width, maximumSize.height)
                                       lineBreakMode:self.lineBreakMode];

    CGRect dateFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, dateStringSize.height);

    self.frame = dateFrame;
}

- (CGFloat)fontSizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size {
    CGFloat fontSize = [font pointSize];
    CGFloat height = [self.text sizeWithFont:font             
                           constrainedToSize:CGSizeMake(size.width,FLT_MAX)  
                               lineBreakMode:UILineBreakModeWordWrap].height;
    UIFont *newFont = font;

    //Reduce font size while too large, break if no height (empty string)
    while (height > size.height && height != 0 && fontSize > self.minimumFontSize) { 
        fontSize--;  
        newFont = [UIFont fontWithName:font.fontName size:fontSize];   
        height = [self.text sizeWithFont:newFont  
                       constrainedToSize:CGSizeMake(size.width,FLT_MAX) 
                           lineBreakMode:UILineBreakModeWordWrap].height;
    };

    // Loop through words in string and resize to fit
    if (fontSize > self.minimumFontSize) {
        for (NSString *word in [self.text componentsSeparatedByString:@" "]) {
            CGFloat width = [word sizeWithFont:newFont].width;
            while (width > size.width && width != 0 && fontSize > self.minimumFontSize) {
                fontSize--;
                newFont = [UIFont fontWithName:font.fontName size:fontSize];   
                width = [word sizeWithFont:newFont].width;
            }
        }
    }
    return fontSize;
}

-(void)setText:(NSString *)text {
    [super setText:text];
    if (originalSize.height == 0) {
        originalPointSize = self.font.pointSize;
        originalSize = self.frame.size;
    }

    if (self.adjustsFontSizeToFitWidth && self.numberOfLines > 1) {
        UIFont *origFont = [UIFont fontWithName:self.font.fontName size:originalPointSize];
        self.font = [UIFont fontWithName:origFont.fontName size:[self fontSizeWithFont:origFont constrainedToSize:originalSize]];
    }

    if (self.alignTextOnTop) [self verticalAlignTop];
}

-(void)setAlignTextOnTop:(BOOL)flag {
    alignTextOnTop = YES;
    if (alignTextOnTop && self.text != nil)
        [self verticalAlignTop];
}

@end

Надеюсь, это поможет.

Ответ 5

В комментариях есть расширение ObjC, которое вычисляет шрифты, необходимые для соответствия многострочного текста в UILabel. Он был переписан в Swift (начиная с 2016 года):

//
//  NSString+KBAdditions.swift
//
//  Created by Alexander Mayatsky on 16/03/16.
//
//  Original code from http://stackoverflow.com/a/4383281/463892 & http://stackoverflow.com/a/18951386
//

import Foundation
import UIKit

protocol NSStringKBAdditions {
    func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat
}

extension NSString : NSStringKBAdditions {
    func fontSizeWithFont(font: UIFont, constrainedToSize size: CGSize, minimumScaleFactor: CGFloat) -> CGFloat {
        var fontSize = font.pointSize
        let minimumFontSize = fontSize * minimumScaleFactor


        var attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: font])
        var height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height

        var newFont = font
        //Reduce font size while too large, break if no height (empty string)
        while (height > size.height && height != 0 && fontSize > minimumFontSize) {
            fontSize--;
            newFont = UIFont(name: font.fontName, size: fontSize)!

            attributedText = NSAttributedString(string: self as String, attributes:[NSFontAttributeName: newFont])
            height = attributedText.boundingRectWithSize(CGSize(width: size.width, height: CGFloat.max), options:NSStringDrawingOptions.UsesLineFragmentOrigin, context:nil).size.height
        }

        // Loop through words in string and resize to fit
        for word in self.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) {
            var width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width
            while (width > size.width && width != 0 && fontSize > minimumFontSize) {
                fontSize--
                newFont = UIFont(name: font.fontName, size: fontSize)!
                width = word.sizeWithAttributes([NSFontAttributeName:newFont]).width
            }
        }
        return fontSize;
    }
}

Ссылка на полный код: https://gist.github.com/amayatsky/e6125a2288cc2e4f1bbf