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

5

12

I want to get the font size of some text after it's been scaled down in a UILabel or UITextField. This was possible before iOS 7.0: How to get UILabel (UITextView) auto adjusted font size?. However, sizeWithFont has been deprecated in iOS 7.0. I've tried using its replacement, sizeWithAttributes, but with no success. Is there any way to do this in iOS 7.0?

Cynde answered 2/2, 2015 at 18:13 Comment(0)
N
3

Swift 4

The answers above work well, but use some deprecated values/functions. Here's a fixed version that works in 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
}
Nonesuch answered 2/4, 2018 at 17:46 Comment(0)
C
11

Here's a function I made to get the adjusted font size of a 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;
    }
}
Cynde answered 2/2, 2015 at 19:39 Comment(3)
I got infinity loop using objective c code in while blockGillies
This is working only for labels with one linePolyethylene
Thanks, but do you have any solution for multiline UILabel? cause this doesn't work.Amann
A
11

I really liked @WaltersGE1's answer, so I made it into an extension for UILabel to make it even more convenient to use:

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: [https://mcmap.net/q/153224/-how-do-i-get-auto-adjusted-font-size-in-ios-7-0-or-later](https://mcmap.net/q/153224/-how-do-i-get-auto-adjusted-font-size-in-ios-7-0-or-later)
    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
        }
    }
}
Askja answered 14/9, 2015 at 14:21 Comment(3)
Thanks. It works, but could/should be optimized by halving the pointSize difference each time instead of decrementing it.Cynde
Doesn't work for me. Always returns 0. This is for the titleLabel within a button.Protrude
Works for me :-)Cryptonym
N
3

Swift 4

The answers above work well, but use some deprecated values/functions. Here's a fixed version that works in 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
}
Nonesuch answered 2/4, 2018 at 17:46 Comment(0)
M
2

Check after the text has been applied (perhaps force a view reload first) you can grab the font size. For instance with a UITextField, you could implement the delegate method for textDidChange and then find out the font size from the font attribute on the textField. For a label, you could update the text then check the font size.

Example I built a project which updated a UILabel from a UITextField

-(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 );
}

The actualSize seems right. (size calculation source)

Missie answered 2/2, 2015 at 18:17 Comment(2)
I know how to auto shrink text. My problem is that I need to get the font size after the minimum font size/scale has been applied.Cynde
Thanks for the idea. There were few problems with that solution, which I correct in my answer.Cynde
S
1

All the solutions so far assume integer font sizes and fail to respect UILabel's minimumScaleFactor property.

Something like this should work:

+ (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;
}
Shadowgraph answered 25/3, 2016 at 23:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.