如何在 iOS 7.0 或更高版本中获得自动调整的字体大小?

How do I get auto-adjusted font size in iOS 7.0 or later?

我想获取某些文本在 UILabel 或 UITextField 中缩小后的字体大小。这在 iOS 7.0 之前是可能的:How to get UILabel (UITextView) auto adjusted font size?。但是,iOS 7.0 中已弃用 sizeWithFont。我试过使用它的替代品 sizeWithAttributes,但没有成功。在 iOS 7.0 中有什么方法可以做到这一点吗?

在应用文本后检查(可能先强制重新加载视图)您可以获取字体大小。例如,对于 UITextField,您可以实现 textDidChange 的委托方法,然后从 textField 的字体属性中找出字体大小。对于标签,您可以更新文本然后检查字体大小。

示例我构建了一个从 UITextField 更新 UILabel 的项目

-(void)textFieldDidEndEditing:(UITextField *)textField{

   self.label.text = textField.text;

    CGSize initialSize = [_label.text sizeWithAttributes:@{NSFontAttributeName:_label.font}];
    while ( initialSize.width > _label.frame.size.width ) {
        [_label setFont:[_label.font fontWithSize:_label.font.pointSize - 1]];
        initialSize = [_label.text sizeWithAttributes:@{NSFontAttributeName:_label.font}];
    }
    CGFloat actualSize = _label.font.pointSize;
    NSLog(@"acutal size %f",actualSize );
}

actualSize 似乎是正确的。 (size calculation source)

这是我为获取 UILabel 的调整字体大小而创建的函数:

Swift

func getApproximateAdjustedFontSizeWithLabel(label: UILabel) -> CGFloat {

    if label.adjustsFontSizeToFitWidth == true {

        var currentFont: UIFont = label.font
        let originalFontSize = currentFont.pointSize
        var currentSize: CGSize = (label.text! as NSString).sizeWithAttributes([NSFontAttributeName: currentFont])

        while currentSize.width > label.frame.size.width && currentFont.pointSize > (originalFontSize * label.minimumScaleFactor) {
            currentFont = currentFont.fontWithSize(currentFont.pointSize - 1)
            currentSize = (label.text! as NSString).sizeWithAttributes([NSFontAttributeName: currentFont])
        }

        return currentFont.pointSize

    }
    else {

        return label.font.pointSize

    }

}

Objective-C

- (CGFloat)getApproximateAdjustedFontSizeWithLabel:(UILabel *)label {

    if (label.adjustsFontSizeToFitWidth) {

        UIFont *currentFont = label.font;
        CGFloat originalFontSize = currentFont.pointSize;
        CGSize currentSize = [label.text sizeWithAttributes:@{NSFontAttributeName : currentFont}];

        while (currentSize.width > label.frame.size.width && currentFont.pointSize > (originalFontSize * label.minimumScaleFactor)) {
            currentFont = [currentFont fontWithSize:currentFont.pointSize - 1];
            currentSize = [label.text sizeWithAttributes:@{NSFontAttributeName : currentFont}];
        }

        return currentFont.pointSize;
    }
    else {

        return label.font.pointSize;
    }
}

我很喜欢@WaltersGE1 的回答,所以我把它做成了 UILabel 的扩展,使用起来更方便:

extension UILabel {

    /// The receiver’s font size, including any adjustment made to fit to width. (read-only)
    ///
    /// If `adjustsFontSizeToFitWidth` is not `true`, this is just an alias for
    /// `.font.pointSize`. If it is `true`, it returns the adjusted font size.
    ///
    /// Derived from: [
    var fontSize: CGFloat {
        get {
            if adjustsFontSizeToFitWidth {
                var currentFont: UIFont = font
                let originalFontSize = currentFont.pointSize
                var currentSize: CGSize = (text! as NSString).sizeWithAttributes([NSFontAttributeName: currentFont])

                while currentSize.width > frame.size.width && currentFont.pointSize > (originalFontSize * minimumScaleFactor) {
                    currentFont = currentFont.fontWithSize(currentFont.pointSize - 1)
                    currentSize = (text! as NSString).sizeWithAttributes([NSFontAttributeName: currentFont])
                }

                return currentFont.pointSize
            }

            return font.pointSize
        }
    }
}

目前所有的解决方案都采用整数字体大小并且不符合 UILabelminimumScaleFactor 属性.

像这样的东西应该可以工作:

+ (CGFloat)fontSizeForLabel:(UILabel *)label 
{
    if (label.adjustsFontSizeToFitWidth == NO || label.minimumScaleFactor >= 1.f) {
        // font adjustment is disabled
        return label.font.pointSize;
    }

    CGSize unadjustedSize = [_label.text sizeWithAttributes:@{NSFontAttributeName:_label.font}];
    CGFloat scaleFactor = label.frame.size.width / unadjustedSize.width;

    if (scaleFactor >= 1.f) {
        // the text already fits at full font size
        return label.font.pointSize;
    }

    // Respect minimumScaleFactor
    scaleFactor = MAX(scaleFactor, minimumScaleFactor);

    CGFloat newFontSize = label.font.pointSize * scaleFactor;

    // Uncomment this if you insist on integer font sizes
    //newFontSize = floor(newFontSize);

    return newFontSize;
}

Swift 4

上面的答案很有效,但使用了一些已弃用的 values/functions。这是 2018 年可用的固定版本。

func approximateAdjustedFontSizeWithLabel(_ label: UILabel) -> CGFloat {
    var currentFont: UIFont = label.font
    let originalFontSize = currentFont.pointSize
    var currentSize: CGSize = (label.text! as NSString).size(withAttributes: [NSAttributedStringKey.font: currentFont])

    while currentSize.width > label.frame.size.width && currentFont.pointSize > (originalFontSize * label.minimumScaleFactor) {
        currentFont = currentFont.withSize(currentFont.pointSize - 1.0)
        currentSize = (label.text! as NSString).size(withAttributes: [NSAttributedStringKey.font: currentFont])
    }

    return currentFont.pointSize
}