I ran in to trouble with UILabel’s sizeWithFont while trying to determine how many CoreText columns will I need for my text. Apparently UILabel and CoreText, while uses same font and font size, has a little bit different line heights and that messes up any calculations. I was quite desperate and too lazy to study various CoreText related references, so I ended up drawing invisible frames filled with text and counting them to get correct number of frames I will actually need.
Today I finally discovered CTFramesetterSuggestFrameSizeWithConstraints which can help with two important things: to get size of the frame you need for your CFAttributedString or to get how many characters of your string would fill frame with particular CGSize.
So, here is some code to illustrate different height results from both functions:
// test string
NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent pellentesque viverra est, molestie vehicula turpis consectetur sed. In hac habitasse platea dictumst. Maecenas vel dolor dolor, et hendrerit libero. Aliquam malesuada, erat vitae tempus tincidunt, ipsum tortor ultrices nunc, a rhoncus diam ligula nec purus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec nec nisi libero. Phasellus viverra imperdiet urna. Ut mollis vulputate metus sed sollicitudin. Aenean viverra tellus vitae tortor placerat imperdiet. Integer porta magna ut mauris euismod in tincidunt justo ornare. Donec ac leo augue. Quisque luctus vehicula nisi id ornare. Pellentesque quis mi ac arcu semper cursus at eu purus. ";
CGContextRef context = (CGContextRef)UIGraphicsGetCurrentContext();
// creating and formatting CFMutableAttributedString
CFMutableAttributedStringRef attrStr = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrStr, CFRangeMake(0, 0), (CFStringRef) text);
CTFontRef font = CTFontCreateWithName(CFSTR("Times New Roman"), fontSize, NULL);
CTTextAlignment alignment = kCTJustifiedTextAlignment;
CTParagraphStyleSetting _settings[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment} };
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(_settings, sizeof(_settings) / sizeof(_settings[0]));
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle);
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, font);
CFRelease(paragraphStyle);
CFRelease(font);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrStr);
CFRelease(attrStr);
int textLength = [text length];
CFRange range;
CGFloat maxWidth = 100.0f;
CGFloat maxHeight = 10000.0f;
CGSize constraint = CGSizeMake(maxWidth, maxHeight);
// checking frame sizes
CGSize labelSize = [article.body sizeWithFont:[UIFont fontWithName:@"Times New Roman" size:16] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
CGSize coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, textLength), nil, constraint, &range);
// printing results
NSLog(@"Label.sizeWithFont: %f x %f", labelSize.width, labelSize.height);
NSLog(@"CoreText.SuggestFrameSize: %f x %f", coreTextSize.width, coreTextSize.height);
NSLog(@"Character count in constraint frame: %d (total: %d)", range.length, textLength);
Results:
Label.sizeWithFont: 100.000000 x 1160.000000 CoreText.SuggestFrameSize: 100.000008 x 1059.507812 Character count in constraint frame: 718 (total: 718)
100px is significant difference and it increases proportionally with text size. Also to demonstrate CFRange usefulness I’ll use 100×100 constraint:
Label.sizeWithFont: 99.000000 x 100.000000 CoreText.SuggestFrameSize: 100.000000 x 84.390625 Character count in constraint frame: 66 (total: 718)
SuggestFrameSize results are interesting in this one. I guess, we get 84.39 height, because CoreText couldn’t fit another line, so updated constraint frame with REAL height.
Tags: CFAttributedString, CFRange, CoreText, CTFramesetterSuggestFrameSizeWithConstraints, iphone-dev, sizeWithFont, UILabel
Did you ever figure out how to fix the problem?