- (void)drawRect:(NSRect)dirtyRect { NSString* str = @"hello kitty, would you tell me why do you want to go to shanghai? 真心伤不起\n\ 楞个圾有圾和在有了上国有和国因为相关爆发\n正常情况下,计算行高只需要ascent+descent+leading即可。\n\ 在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距,\n\ ascent+descent计算出来的Glyph的高度还是正确的。这样就有了第一步在创建用于绘图的CFAttributedStringRef时,\n\ 除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。\n\ 在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实\n\ 际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了";
NSAttributedString* string = [[NSAttributedString alloc] initWithString:str]; int h = [self getAttributedStringHeightWithString:string WidthValue:200]; [str drawInRect:NSMakeRect(0, 0, 200, h) withAttributes:nil];
[string release]; [str release]; }
因为我是在OS X下,而不是iOS下.有些不同以下代码,绘出来的是 NSString *labelString = @"hello kitty, would you tell me why do you want to go to shanghai? 真心伤不起\n\ 楞个圾有圾和在有了上国有和国因为相关爆发正常情况下,计算行高只需要ascent+descent+leading即可。\n\ 在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距,\n\ ascent+descent计算出来的Glyph的高度还是正确的。这样就有了第一步在创建用于绘图的CFAttributedStringRef时,\n\ 除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。\n\ 在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实\n\ 际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了";
NSSize labelSize = [labelString sizeWithAttributes:attributes]; NSRect labelRect = NSMakeRect(0, 0, labelSize.width, labelSize.height); [labelString drawInRect:labelRect withAttributes:attributes]; /////////////////////////// hello kitty, would you tell me why do you want to go to shanghai? 真心伤不起 楞个圾有圾和在有了上国有和国因为相关爆发正常情况下,计算行高只需要ascent+descent+leading即可。 在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距, ascent+descent计算出来的Glyph的高度还是正确的。这样就有了第一步在创建用于绘图的CFAttributedStringRef时, 除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。 在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实 际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了////////////////////////// 没有在200处分行,实事上我需要分行,效果如下,怎么实现呢?/////////////////////// hello kitty, would you tell me why do you want to go to shanghai? 真心伤不起 楞个圾有圾和在有了上国有和国因为相关 爆发正常情况下,计算行高只需要ascent+ descent+leading即可。 在这个略有不同的情况下,leading的值会 出现偏差,导致算出来的结果是错误的。如果不 管行距, ascent+descent计算出来的Glyph的高度 还是正确的。这样就有了第一步在创建用于绘图 的CFAttributedStringRef时, 除了设置字体,多设置一个 CTParagraphStyleRef,其中特别应该确定行距 kCTParagraphStyleSpecifierLineSpacing。 在计算这里时,先逐行计算ascent+ descent,累加起来,再加上一个行数*之前设置 好的行距,这样算出来的就是这些文本的实 际高度, CTLineGetTypographicBounds返回的结果是宽 度,这样就可得到文本实际填充面积的Rect了
此方法可以计算出文字放到一个固定的(constrainedToSize)大小里面的宽高;
比如你的:CGSize strSize = [str sizeWithFont:[UIFont systemFontOfSize:16]
constrainedToSize:CGSizeMake(100, CGFLOAT_MAX)
lineBreakMode:UILineBreakModeCharacterWrap];
///////////////////////////////////////////
Core Text提供了一系列方便的函数,可以很容易的把文本绘制在屏幕上,对于一个Frame来说,一般并不需要担心文本的排列问题,这些Core Text的函数都可以直接搞定,只要给他一个大小合适的CGRect就可以。
但,在某些情况下,我们还希望知道这段文本在绘制之后,对应绘制的字体字号设置,在屏幕上实际占用了多大面积。举例来说,有文本段落a,屏幕大小 rect,通常做法是以rect创建path,然后创建CTFramesetter,再然后创建CTFrame,最后用CTFrameDraw画出来,这 时候,往往文本段落占用的实际面积会小于rect,这时候就有必要获得这段文本所占用的真正面积。
最理想的情况是使用
double CTLineGetTypographicBounds( CTLineRef line, CGFloat* ascent, CGFloat* descent, CGFloat* leading );
这是Core Text提供的函数,传入CTLine,就会得到这一行的ascent,descent和leading,在OSX上通常可以工作的很好,但是在 iOS(iPhone/iPad)上这个函数的结果略有不同。
正常情况下,计算行高只需要ascent+descent+leading即可。在这个略有不同的情况下,leading的值会出现偏差,导致算出 来的结果是错误的。如果不管行距,ascent+descent计算出来的Glyph的高度还是正确的。
这样就有了第一步
在创建用于绘图的CFAttributedStringRef时,除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该 确定行距kCTParagraphStyleSpecifierLineSpacing。在计算这里时,先逐行计算ascent+descent,累加起 来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实际高度,CTLineGetTypographicBounds返回的结果是宽度, 这样就可得到文本实际填充面积的Rect了。
但是这还是有问题,因为OSX上和iOS处理不同,所以事实上iOS的模拟器和真机的最终效果是不一样的,这样调试程序很麻烦。
于是还需要第二步
在最终往页面上绘制的时候,不再用CTFrameDraw来一次绘制全部,而是使用CTLineDraw逐行画,在画之前,先用 CGContextSetTextPosition来设置好每行文本的位置。这样就保证了在任何平台上绘制效果一致。
问题就解决了。
CoreText在OS X和iOS上实现有很多细节区别,比如说,对CTRun的划分方式也不一样,在iOS上划分出来的Run数量比OSX上少很多,是按照字体划分的Run, 按照文档出来,这是正确的,但OSX上是一个字一个Run这样划分的,和文档不符。iOS上的处理显然效率更好。
stackoverflow 上有一个类 似的讨论,不过没考虑到iOS的区别
//以下代码没有正确计算出文字的高度...大家看看哪里出错了?
- (int)getAttributedStringHeightWithString:(NSAttributedString*)string WidthValue:(int)width {
int h = 10000; //[string length] * 40;
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string); //string为要计算高度的NSAttributedString
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0, 0, width, h)); //这里的高要设置足够大
CTFrameRef textFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
CGPathRelease(path);
CFRelease(framesetter);
NSArray* linesArray = (NSArray*)CTFrameGetLines(textFrame);
NSUInteger count = [linesArray count];
if(count <= 0) {
CFRelease(textFrame);
return 0;
}
CGPoint origins[count];
CTFrameGetLineOrigins(textFrame, CFRangeMake(0, 0), origins);
CGFloat line_y = origins[count - 1].y; //最后一行line的原点y坐标
CGFloat ascent, descent, leading;
CTLineRef line = (CTLineRef)[linesArray objectAtIndex: [linesArray count] - 1];
CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
CGFloat total_height = h - line_y + ascent + descent + leading + 1; //+1为了纠正descent转换成int小数点后舍去的值
CFRelease(textFrame);
return total_height;
}
- (void)drawRect:(NSRect)dirtyRect {
NSString* str = @"hello kitty, would you tell me why do you want to go to shanghai? 真心伤不起\n\
楞个圾有圾和在有了上国有和国因为相关爆发\n正常情况下,计算行高只需要ascent+descent+leading即可。\n\
在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距,\n\
ascent+descent计算出来的Glyph的高度还是正确的。这样就有了第一步在创建用于绘图的CFAttributedStringRef时,\n\
除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。\n\
在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实\n\
际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了";
NSAttributedString* string = [[NSAttributedString alloc] initWithString:str];
int h = [self getAttributedStringHeightWithString:string WidthValue:200];
[str drawInRect:NSMakeRect(0, 0, 200, h) withAttributes:nil];
[string release];
[str release];
}
NSString *labelString = @"hello kitty, would you tell me why do you want to go to shanghai? 真心伤不起\n\
楞个圾有圾和在有了上国有和国因为相关爆发正常情况下,计算行高只需要ascent+descent+leading即可。\n\
在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距,\n\
ascent+descent计算出来的Glyph的高度还是正确的。这样就有了第一步在创建用于绘图的CFAttributedStringRef时,\n\
除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。\n\
在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实\n\
际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了";
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]], NSFontAttributeName,
//[NSColor redColor], NSForegroundColorAttributeName,
//[NSColor yellowColor], NSBackgroundColorAttributeName,
nil];
NSSize labelSize = [labelString sizeWithAttributes:attributes];
NSRect labelRect = NSMakeRect(0, 0, labelSize.width, labelSize.height);
[labelString drawInRect:labelRect withAttributes:attributes];
///////////////////////////
hello kitty, would you tell me why do you want to go to shanghai? 真心伤不起
楞个圾有圾和在有了上国有和国因为相关爆发正常情况下,计算行高只需要ascent+descent+leading即可。
在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距,
ascent+descent计算出来的Glyph的高度还是正确的。这样就有了第一步在创建用于绘图的CFAttributedStringRef时,
除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。
在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实
际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了//////////////////////////
没有在200处分行,实事上我需要分行,效果如下,怎么实现呢?///////////////////////
hello kitty, would you tell me why do you
want to go to shanghai? 真心伤不起
楞个圾有圾和在有了上国有和国因为相关
爆发正常情况下,计算行高只需要ascent+
descent+leading即可。
在这个略有不同的情况下,leading的值会
出现偏差,导致算出来的结果是错误的。如果不
管行距,
ascent+descent计算出来的Glyph的高度
还是正确的。这样就有了第一步在创建用于绘图
的CFAttributedStringRef时,
除了设置字体,多设置一个
CTParagraphStyleRef,其中特别应该确定行距
kCTParagraphStyleSpecifierLineSpacing。
在计算这里时,先逐行计算ascent+
descent,累加起来,再加上一个行数*之前设置
好的行距,这样算出来的就是这些文本的实
际高度,
CTLineGetTypographicBounds返回的结果是宽
度,这样就可得到文本实际填充面积的Rect了