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

前端 未结 14 772
独厮守ぢ
独厮守ぢ 2020-11-28 07:00

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 anythin

14条回答
  •  生来不讨喜
    2020-11-28 07:05

    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];
        }
    }
    

提交回复
热议问题