Calculate Font Size to Fit Frame - Core Text - NSAttributedString - iOS

I have some text which I am drawing into a fixed frame via an NSAttributedString (code below). At the moment I am hard coding the text size to 16. My question is, is there a way to calculate the best fit size for the text for the given frame ?

- (void)drawText:(CGContextRef)contextP startX:(float)x startY:(float)
y withText:(NSString *)standString
{
    CGContextTranslateCTM(contextP, 0, (bottom-top)*2);
    CGContextScaleCTM(contextP, 1.0, -1.0);

    CGRect frameText = CGRectMake(1, 0, (right-left)*2, (bottom-top)*2);

    NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:standString];
    [attrString addAttribute:NSFontAttributeName
                      value:[UIFont fontWithName:@"Helvetica-Bold" size:16.0]
                      range:NSMakeRange(0, attrString.length)];

    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString));
    struct CGPath * p = CGPathCreateMutable();
    CGPathAddRect(p, NULL, frameText);
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0), p, NULL);

    CTFrameDraw(frame, contextP);
}

Answers


The only way I can see this being possible is to have a system that runs the size calculation then adjusts the size and repeats until it finds the right size.

I.e. set up a bisecting algorithm that goes between certain sizes.

i.e. run it for size 10. Too small. Size 20. Too small. Size 30. Too big. Size 25. Too small. Size 27. Just right, use size 27.

You could even start in hundreds.

Size 100. Too big. Size 50. etc...


Here is a simple piece of code that will figure out the maximum font size to fit within the bounds of a frame:

UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.text = @"Some text";
float largestFontSize = 12;
while ([label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:largestFontSize]}].width > modifierFrame.size.width)
{
     largestFontSize--;
}
label.font = [UIFont systemFontOfSize:largestFontSize];

You could use sizeWithFont :

[myString sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:24]   
constrainedToSize:CGSizeMake(293, 10000)] // put the size of your frame

But it is deprecated in iOS 7, so I recommend if working with string in UILabel :

[string sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}];

If you are working with a rect :

CGRect textRect = [text boundingRectWithSize:mySize
                                 options:NSStringDrawingUsesLineFragmentOrigin
                              attributes:@{NSFontAttributeName:FONT}
                                 context:nil];

CGSize size = textRect.size;

The currently accepted answer talks of an algorithm, but iOS provides calculations for an NSString object. I would use sizeWithAttributes: of the NSString class.

sizeWithAttributes:

Returns the bounding box size the receiver occupies when drawn with the given attributes.

    - (CGSize)sizeWithAttributes:(NSDictionary *)attributes

Source: Apple Docs - NSString UIKit Additions Reference

EDIT Misinterpreted the question, so this answer is off the mark.


This is the code to have dynamic font size changing by the frame width, using the logic from the other answers. The while loop might be dangerous, so please donot hesitate to submit improvements.

float fontSize = 17.0f; //initial font size
CGSize rect;
while (1) {
   fontSize = fontSize+0.1;
   rect = [watermarkText sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}];
    if ((int)rect.width == (int)subtitle1Text.frame.size.width) {
        break;
    }
}
subtitle1Text.fontSize = fontSize;

Even more easy/faster (but of course approximate) way would be this:

class func calculateOptimalFontSize(textLength:CGFloat, boundingBox:CGRect) -> CGFloat
    {
        let area:CGFloat = boundingBox.width * boundingBox.height
        return sqrt(area / textLength)
    }

We are assuming each char is N x N pixels, so we just calculate how many times N x N goes inside bounding box.


You can set the UILabel's property adjustsFontSizeToFitWidth to YES as per Apple's documentation


Need Your Help

Make tabs movable in Qt

c++ qt tabs docking

I want to make an application in Qt where the tabs in the QTabBar can be "undocked" so they will become a floating window, or can be dragged into another QTabBar and "transfer" into that QTabBar. M...

How to use MYSQL indexes in python 2.7 MySQLdb

python mysql sql python-2.7 mysql-python

indie = '''CREATE FULLTEXT yay ON panda (Question, Answer);'''

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.