NSString font size specific to the frame width

I am using drawRect

for text display by calling NSString

. I am trying to implement using sizeWithFont

to automatically resize the font (shrink) with the default font size of 17 and using a loop to reduce the font size by 1 if it doesn't fit the width. Can anyone help me how to implement this? An example would be good right now, I only have the font size set to 17.0

[[self.string displayName] drawAtPoint:CGPointMake(xcoord, ycoord) withFont:[UIFont boldSystemFontOfSize:17.0]];
CGSize size = [[self.patient displayName] sizeWithFont:[UIFont boldSystemFontOfSize:17.0]];
max_current_y = size.height > max_current_y ? size.height : max_current_y;
xcoord = xcoord + 3.0f + size.width;

      

+3


source to share


7 replies


OK, never mind. Here's a modified version of the same method as NSString for returning a font:

    -(UIFont*)getFontForString:(NSString*)string
               toFitInRect:(CGRect)rect
                  seedFont:(UIFont*)seedFont{
    UIFont* returnFont = seedFont;
    CGSize stringSize = [string sizeWithAttributes:@{NSFontAttributeName : seedFont}];

    while(stringSize.width > rect.size.width){
        returnFont = [UIFont systemFontOfSize:returnFont.pointSize -1];
        stringSize = [string sizeWithAttributes:@{NSFontAttributeName : returnFont}];
    }

    return returnFont;
}

      

Here's how to call it:



NSString* stringToDraw = @"Test 123";

    CGRect rect = CGRectMake(100., 100., 100., 200.);
    UIFont* font = [self getFontForString:stringToDraw toFitInRect:rect seedFont:[UIFont systemFontOfSize:20]];
    [stringToDraw drawInRect:rect withFont:font];

      

Code for iOS7 +

+12


source


Trying font sizes in 1.0 increments can be very slow. You can improve the algorithm significantly by doing two measures for two different sizes, then using linear approximation to guess the size that is very close to the correct one.

If this is not close enough, repeat the calculation using the guessed size instead of one of the previous two until it is good enough or stops changing:



// any values will do, prefer those near expected min and max
CGFloat size1 = 12.0, size2 = 56.0; 
CGFloat width1 = measure_for_size(size1);
CGFloat width2 = measure_for_size(size2);

while (1) {
    CGFloat guessed_size = size1 + (required_width - width1) * (size2 - size1) / (width2 - width1);

    width2 = measure_for_size(guessed_size);
    if ( fabs(guessed_size-size2) < some_epsilon || !is_close_enough(width2, required_width) ) {
        size2 = guessed_size;
        continue;
    }
    // round down to integer and clamp guessed_size as appropriate for your design
    return floor(clamp(guessed_size, 6.0, 24.0));
}

      

is_close_enough()

the implementation is entirely up to you. Given that the width of the text grows almost linearly with the font size, you can just drop it and just do 2-4 iterations, which should be enough.

+9


source


I wanted to try making a version that didn't require checking font sizes repeatedly using a do ... while loop. Instead, I assumed the font size was linear, then worked out the size difference between the required frame width and the actual frame width, and then adjusted the font size accordingly. So I got this function:

+ (CGFloat)fontSizeToFitString:(NSString *)string inWidth:(float)width withFont:(UIFont *)font
{
    UILabel *label = [UILabel new];
    label.font = font;
    label.text = string;
    [label sizeToFit];

    float ratio = width / label.frame.size.width;
    return font.pointSize * ratio;
}

      

Pass in a font of any size, as well as a string and desired width, and it will return you the point size for that font.

I also wanted to dwell a little more and find out the font size for a multi-line line so that the longest line will fit without a line break:

+ (CGFloat)fontSizeToFitLongestLineOfString:(NSString *)string inWidth:(float)width withFont:(UIFont *)font
{
    NSArray *stringLines = [string componentsSeparatedByString:@"\n"];

    UILabel *label = [UILabel new];
    label.font = font;

    float maxWidth = 0;

    for(NSString *line in stringLines)
    {
        label.text = line;
        [label sizeToFit];
        maxWidth = MAX(maxWidth, label.frame.size.width);
    }

    float ratio = width / maxWidth;
    return font.pointSize * ratio;
}

      

Everything seems to work fine for me. Hope this helps someone else.

+5


source


The original poster did not specify what platform it was running on, but for OSX developers on Mavericks sizeWithFont:

does not exist and one should use sizeWithAttributes

:

NSSize newSize = [aString sizeWithAttributes:
[NSDictionary dictionaryWithObjectsAndKeys:
           [NSFont fontWithName:@"Arial Rounded MT Bold" size:53.0],NSFontAttributeName,nil
]];

      

+1


source


Here's a way that can return you a font that will fit in rect:

-(UIFont*)getFontToFitInRect:(CGRect)rect seedFont:(UIFont*)seedFont{
    UIFont* returnFont = seedFont;
    CGSize stringSize = [self sizeWithFont:returnFont];

    while(stringSize.width > rect.size.width){
        returnFont = [UIFont systemFontOfSize:returnFont.pointSize -1];
        stringSize = [self sizeWithFont:returnFont];
    }

    return returnFont;
}

      

You can add this method to the NSString category. You can read more about how to add a category here: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210 -CH6-SW2

If you don't want to create a category, you can add this method to one of your utility classes and pass in the string for which you want the font to be returned.

0


source


Here is another method inspired by @ puru020 and @jowie's answers. Hope this helps someone.

-(UIFont *) adjustedFontSizeForString:(NSString *)string forWidth:(float)originalWidth forFont:(UIFont *)font
{
    CGSize stringSize = [string sizeWithFont:font];
    if(stringSize.width <= originalWidth)
    {
        return font;
    }
    float ratio = originalWidth / stringSize.width;
    float fontSize = font.pointSize * ratio;
    return [font fontWithSize:fontSize];
}

      

0


source


I modified @ puru020's solution a bit, added support for attributes and improved a bit:

Note. The method must be wrapped in an NSString category

- (UIFont*)requiredFontToFitInSize:(CGSize)size seedFont:(UIFont*)seedFont attributes:(NSDictionary*)attributes{
   UIFont *returnFont = [UIFont systemFontOfSize:seedFont.pointSize +1];
   NSMutableDictionary *mutableAttributes = attributes.mutableCopy;
   CGSize stringSize;

   do {
    returnFont = [UIFont systemFontOfSize:returnFont.pointSize -1];
    [mutableAttributes setObject:returnFont forKey:NSFontAttributeName];
    stringSize = [self sizeWithAttributes:mutableAttributes];
   } while (stringSize.width > size.width);

   return returnFont;
}

      

0


source







All Articles