views:

2112

answers:

6

I'm trying to measure the visual size of a NSString that takes into account the number of lines I can render. However, sizeWithFont doesn't take into account numberOfLines property? So my layout algorithm positions everything lower than they actually need to be.

_price = [[UILabel alloc] init];
_price.text = myPriceValue;
_price.lineBreakMode = UILineBreakModeWordWrap;
_price.numberOfLines = 3;
_price.backgroundColor = [UIColor clearColor];
_price.textColor = TTSTYLEVAR(colorPrice);
/// the follow code ignores numberOfLines and just tells me the size of the whole block.  
// I'd like it to be aware of numberOfLines
//
CGSize priceSize = [_price.text sizeWithFont:_price.font
        constrainedToSize:CGSizeMake(maxWidth, CGFLOAT_MAX)
        lineBreakMode:UILineBreakModeWordWrap];

Does anyone know how to do this using the iPhone SDK?

A: 

Of course it doesn't take it into account, since nothing being called or passed in has that information. You're strictly working with strings, sizes, and fonts. It's the label that has the number of lines in it.

I'm not sure what exactly your problem is; are you getting a size that's too tall or too short, or what? You can find out the number of lines of text by dividing the height of the result by the height of the font, which is the value of the ascender plus the descender, I believe.

Ed Marty
Actually, sizeWithFont:constainedToSize:lineBreakMode takes into account a bounding box and word wrapping.Note that sizeWithFont:forWidth:lineBreakMode does NOT work with multiple lines.
Jason Moore
A: 

Instead of trying to do it in one call, do something like this (pardon the pseudocode, it's late):

NSString *s = _price.text;
UIFont *font = _price.font;
CGFloat fontSize = font.pointSize;

while (TRUE)
{
   CGSize priceSize = [s sizeWithFont: font constrainedToSize: 
       CGSizeMake(maxWidth, fontSize) lineBreakMode: UILineBreakModeWordWrap];

    if ( /* priceSize is satisfactory */ )
    {
        break; // Make sure this exits, eventually!!!
    }
    fontSize -= 1.0; // or a smaller decrement if you like
    font = // new, smaller font

}
Amagrammer
A: 

The correct answer is, of course, you need to set numberOfLines to 0, which will cause the framework to compute the result with however many lines it needs. See also this question.

Tim Keating
Nope, number of lines to 0 is infinite lines. but i want the height of 3 lines
CVertex
Ah, I see. I misunderstood the question.
Tim Keating
+2  A: 

Instead of CGFLOAT_MAX for the max height of your text calculation, try getting the size of one line with this:

[_price.text sizeWithFont:_price.font].height

and then multiplying that by the maximum # of lines you want, then plugging that into the height of the size you are constraining yourself to. It'd probably look like this:

_price = [[UILabel alloc] init];
_price.text = myPriceValue;
_price.lineBreakMode = UILineBreakModeWordWrap;
_price.numberOfLines = 3;
_price.backgroundColor = [UIColor clearColor];
_price.textColor = TTSTYLEVAR(colorPrice);
CGFloat lineHeight = [_price.text sizeWithFont:_price.font].height;
CGSize priceSize = [_price.text sizeWithFont:_price.font
        constrainedToSize:CGSizeMake(maxWidth, lineHeight * _price.numberOfLines)
        lineBreakMode:UILineBreakModeWordWrap];

Don't use this if you ever set number of lines to 0 as your max height will be 0 in that case; you should use CGFLOAT_MAX then.

Kevlar
A: 

Use the UILabel's sizeToFit instead of sizeWithFont: to layout a multi-line UILabel, since sizeWithFont: will truncate the string (see apple docs). The following code reduces the font size of a label until the text fit into a the specified size... multiple lines of text will be used as soon as they fit into the specified height:

-(void)setFontSizeOfMultiLineLabel: (UILabel*)label 
        toFitSize: (CGSize) size 
        forMaxFontSize: (CGFloat) maxFontSize 
        andMinFontSize: (CGFloat) minFontSize 
        startCharacterWrapAtSize: (CGFloat)characterWrapSize{

CGRect constraintSize = CGRectMake(0, 0, size.width, 0);
label.frame = constraintSize;
label.lineBreakMode = UILineBreakModeWordWrap;
label.numberOfLines = 0; // allow any number of lines

for (int i = maxFontSize; i > minFontSize; i--) {

    if((i < characterWrapSize) && (label.lineBreakMode == UILineBreakModeWordWrap)){
        // start over again with lineBreakeMode set to character wrap 
        i = maxFontSize;
        label.lineBreakMode = UILineBreakModeCharacterWrap;
    }

    label.font = [label.font fontWithSize:i];
    [label sizeToFit];
    if(label.frame.size.height < size.height){
        break;
    }       
    label.frame = constraintSize;
  } 
}

Call this with a label that has your favorite text and font:

UILabel *label = [[UILabel alloc] initWithFrame: CGRectZero];   
label.backgroundColor = [UIColor clearColor];   
label.textColor = [UIColor whiteColor];
label.text = text;
label.font = [UIFont fontWithName: @"Helvetica" size: 16];
[self setFontSizeOfMultiLineLabel: label toFitSize: CGSizeMake(200, 44) forMaxFontSize: 16 andMinFontSize: 8 startCharacterWrapAtSize: 11]; 

The startCharacterWrapAtSize parameter lets you choose to use characterWrap starting at the giving font size. This should save space in the case wordWrap would use really small fonts.

edit: bugfix

Felix
A: 

This does not work for me, it's adjusting the fontsize, but it linebreak with character wrapping instead of wordwrapping even if i take take that charatcter part away.

someone who can explain this ?

role