Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to adjust font size of label to fit the rectangle?

Tags:

uilabel

iphone

Yeah, there's this cool myLabel.adjustsFontSizeToFitWidth = YES; property. But as soon as the label has two lines or more, it won't resize the text to anything. So it just gets truncated with ... if it doesn't fit into the rect.

Is there another way to do it?

like image 461
dontWatchMyProfile Avatar asked May 16 '10 15:05

dontWatchMyProfile


People also ask

How do I change font size in labels?

To change the font size in HTML, use the style attribute. The style attribute specifies an inline style for an element. The attribute is used with the HTML <p> tag, with the CSS property font-size.


2 Answers

If you want to make sure the label fits in the rectangle both width and height wise you can try different font size on the label to see if one will fit.

This snippet starts at 300 pt and tries to fit the label in the targeted rectangle by reducing the font size.

- (void) sizeLabel: (UILabel *) label toRect: (CGRect) labelRect {      // Set the frame of the label to the targeted rectangle     label.frame = labelRect;      // Try all font sizes from largest to smallest font size     int fontSize = 300;     int minFontSize = 5;      // Fit label width wize     CGSize constraintSize = CGSizeMake(label.frame.size.width, MAXFLOAT);      do {         // Set current font size         label.font = [UIFont fontWithName:label.font.fontName size:fontSize];          // Find label size for current font size         CGRect textRect = [[label text] boundingRectWithSize:constraintSize                                                      options:NSStringDrawingUsesLineFragmentOrigin                                                   attributes:@{NSFontAttributeName: label.font}                                                      context:nil];          CGSize labelSize = textRect.size;          // Done, if created label is within target size         if( labelSize.height <= label.frame.size.height )             break;          // Decrease the font size and try again         fontSize -= 2;      } while (fontSize > minFontSize); } 

I think the above explains what goes on. A faster implementation could use caching and argarcians binary search as follows

+ (CGFloat) fontSizeForString: (NSString*) s inRect: (CGRect) labelRect  {     // Cache repeat queries     static NSMutableDictionary* mutableDict = nil;     static dispatch_once_t onceToken;     dispatch_once(&onceToken, ^{         mutableDict = [NSMutableDictionary dictionary];     });      NSString* key = [NSString stringWithFormat:@"%@_%d_%d", s, (int) labelRect.size.width, (int) labelRect.size.height];     NSNumber* value = [mutableDict objectForKey:key];     if (value)         return value.doubleValue;      // Set the frame of the label to the targeted rectangle     UILabel* label = [[UILabel alloc] init];     label.text = s;     label.frame = labelRect;      // Hopefully between 5 and 300     CGFloat theSize = (CGFloat) [self binarySearchForFontSizeForLabel:label withMinFontSize:5 withMaxFontSize:300 withSize:label.frame.size];     [mutableDict setObject:@(theSize) forKey:key];     return  theSize; }   + (NSInteger)binarySearchForFontSizeForLabel:(UILabel *)label withMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize withSize:(CGSize)size {     // If the sizes are incorrect, return 0, or error, or an assertion.     if (maxFontSize < minFontSize) {         return maxFontSize;     }      // Find the middle     NSInteger fontSize = (minFontSize + maxFontSize) / 2;     // Create the font     UIFont *font = [UIFont fontWithName:label.font.fontName size:fontSize];     // Create a constraint size with max height     CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);     // Find label size for current font size     CGRect rect = [label.text boundingRectWithSize:constraintSize                                            options:NSStringDrawingUsesLineFragmentOrigin                                         attributes:@{NSFontAttributeName : font}                                            context:nil];     CGSize labelSize = rect.size;      // EDIT:  The next block is modified from the original answer posted in SO to consider the width in the decision. This works much better for certain labels that are too thin and were giving bad results.     if (labelSize.height >= (size.height + 10) && labelSize.width >= (size.width + 10) && labelSize.height <= (size.height) && labelSize.width <= (size.width)) {         return fontSize;     } else if (labelSize.height > size.height || labelSize.width > size.width) {         return [self binarySearchForFontSizeForLabel:label withMinFontSize:minFontSize withMaxFontSize:fontSize - 1 withSize:size];     } else {         return [self binarySearchForFontSizeForLabel:label withMinFontSize:fontSize + 1 withMaxFontSize:maxFontSize withSize:size];     } } 
like image 58
Niels Castle Avatar answered Sep 24 '22 21:09

Niels Castle


I found Niels' answer to be the best answer for this issue. However, I have a UIView that can have 100 labels where I need to fit the text, so this process was very inefficient and I could feel the hit in performance.

Here is his code modified to use a binary search instead, rather than a linear search. Now it works very efficiently.

- (NSInteger)binarySearchForFontSizeForLabel:(UILabel *)label withMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize withSize:(CGSize)size {
    // If the sizes are incorrect, return 0, or error, or an assertion.
    if (maxFontSize < minFontSize) {
        return 0;
    }

    // Find the middle
    NSInteger fontSize = (minFontSize + maxFontSize) / 2;
    // Create the font
    UIFont *font = [UIFont fontWithName:label.font.fontName size:fontSize];
    // Create a constraint size with max height
    CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT);
    // Find label size for current font size
    CGRect rect = [label.text boundingRectWithSize:constraintSize
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                        attributes:@{NSFontAttributeName : font}
                                           context:nil];
    CGSize labelSize = rect.size;

    // EDIT:  The next block is modified from the original answer posted in SO to consider the width in the decision. This works much better for certain labels that are too thin and were giving bad results.
    if (labelSize.height >= (size.height + 10) && labelSize.width >= (size.width + 10) && labelSize.height <= (size.height) && labelSize.width <= (size.width)) {
        return fontSize;
    } else if (labelSize.height > size.height || labelSize.width > size.width) {
        return [self binarySearchForFontSizeForLabel:label withMinFontSize:minFontSize withMaxFontSize:fontSize - 1 withSize:size];
    } else {
        return [self binarySearchForFontSizeForLabel:label withMinFontSize:fontSize + 1 withMaxFontSize:maxFontSize withSize:size];
    }
}

- (void)sizeBinaryLabel:(UILabel *)label toRect:(CGRect)labelRect {

    // Set the frame of the label to the targeted rectangle
    label.frame = labelRect;

    // Try all font sizes from largest to smallest font
    int maxFontSize = 300;
    int minFontSize = 5;

    NSInteger size = [self binarySearchForFontSizeForLabel:label withMinFontSize:minFontSize withMaxFontSize:maxFontSize withSize:label.frame.size];

    label.font = [UIFont fontWithName:label.font.fontName size:size];

}

Credit goes also to https://gist.github.com/988219

like image 37
agarcian Avatar answered Sep 24 '22 21:09

agarcian