Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
322 views
in Technique[技术] by (71.8m points)

objective c - How to know if NSString fits in UILabel or not and index of the last string which fits?

I have 4 lines UILabel with exact frame and font.

I need to know if this string fits the label and what is the index of the last character which fits.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The kernel of the answer is in Cupcake's referenced posting. Anyway, you can use sizeWithFont:constrainedToSize:lineBreakMode: to figure out what the size of a frame would be with a particular font in a label of a given width given a specific word wrapping, e.g.

CGSize size = [string sizeWithFont:font constrainedToSize:sizeConstraint lineBreakMode:UILineBreakModeWordWrap];

Set sizeConstraint to be the same width of your label, but set the height to be larger. If the resulting size.height is larger than your UILabel, then your string is too long. Theoretically, you could remove the last character/word and try again and repeat until it fits.

If you think the strings might be very long, you might want to go the other way, start with a short portion of the string and keep adding characters until it's too large, and then you know the last character.

Either way, this iterative calculation of the size can be pretty cpu intensive operation, so be careful.

Update:

Here is an algorithm that returns the length of NSString that can fit into the UILabel in question using the default font (but ignoring minimum font size):

- (NSUInteger)fitString:(NSString *)string intoLabel:(UILabel *)label
{
    UIFont *font           = label.font;
    UILineBreakMode mode   = label.lineBreakMode;

    CGFloat labelWidth     = label.frame.size.width;
    CGFloat labelHeight    = label.frame.size.height;
    CGSize  sizeConstraint = CGSizeMake(labelWidth, CGFLOAT_MAX);

    if ([string sizeWithFont:font constrainedToSize:sizeConstraint lineBreakMode:mode].height > labelHeight) 
    {
        NSString *adjustedString;

        for (NSUInteger i = 1; i < [string length]; i++) 
        {
            adjustedString = [string substringToIndex:i];

            if ([adjustedString sizeWithFont:font constrainedToSize:sizeConstraint lineBreakMode:mode].height > labelHeight)
                return i - 1;
        }
    }

    return [string length];
}

You could probably make this more efficient if you, for example, checked if word break mode, jumping to the next word separator and then calling sizeWithFont, but for small UILabels this might be sufficient. If you wanted to leverage word-wrap logic to minimize the number of times you call sizeWithFont, you might have something like:

- (NSUInteger)fitString:(NSString *)string intoLabel:(UILabel *)label
{
    UIFont *font           = label.font;
    UILineBreakMode mode   = label.lineBreakMode;

    CGFloat labelWidth     = label.frame.size.width;
    CGFloat labelHeight    = label.frame.size.height;
    CGSize  sizeConstraint = CGSizeMake(labelWidth, CGFLOAT_MAX);

    if ([string sizeWithFont:font constrainedToSize:sizeConstraint lineBreakMode:mode].height > labelHeight) 
    {
        NSUInteger index = 0;
        NSUInteger prev;
        NSCharacterSet *characterSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];

        do 
        {
            prev = index;
            if (mode == UILineBreakModeCharacterWrap)
                index++;
            else
                index = [string rangeOfCharacterFromSet:characterSet options:0 range:NSMakeRange(index + 1, [string length] - index - 1)].location;
        }
        while (index != NSNotFound && index < [string length] && [[string substringToIndex:index] sizeWithFont:font constrainedToSize:sizeConstraint lineBreakMode:mode].height <= labelHeight);

        return prev;
    }

    return [string length];
}

Perhaps the character set used here isn't quite right (should you include hyphens, for example), but it's probably pretty close and far more efficient than doing character by character, if you don't need to do that.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...