iOS7.0之后计算文本尺寸的新方法详解

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(nullableNSDictionary<NSString*,id> *)attributes context:(nullableNSStringDrawingContext*)contextNS_AVAILABLE(10_11,7_0);

返回文本绘制所占据的矩形空间。

- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context

参数

size

宽高限制,用于计算文本绘制时占据的矩形块。

The width and height constraints to apply when computing the string’s bounding rectangle.

options

文本绘制时的附加选项。可能取值请参考“NSStringDrawingOptions”。

context

context上下文。包括一些信息,例如如何调整字间距以及缩放。最终,该对象包含的信息将用于文本绘制。该参数可为 nil 。

返回值

一个矩形,大小等于文本绘制完将占据的宽和高。

讨论

可 以使用该方法计算文本绘制所需的空间。size 参数是一个constraint ,用于在绘制文本时作为参考。但是,如果绘制完整个文本需要更大的空间,则返回的矩形大小可能比 size 更大。一般,绘制时会采用constraint 提供的宽度,但高度则会根据需要而定。

特殊情况

为了计算文本块的大小,该方法采用默认基线。

如果 NSStringDrawingUsesLineFragmentOrigin未指定,矩形的高度将被忽略,同时使用单线绘制。(由于一个 bug,在 iOS6 中,宽度会被忽略)

兼容性iOS 6.0 以后支持。

声明于NSStringDrawing.

另外,关于参数(NSStringDrawingOptions)options

typedef NS_ENUM(NSInteger, NSStringDrawingOptions) {

NSStringDrawingTruncatesLastVisibleLine = 1 << 5, // Truncates and adds the ellipsis character to the last visible line if the text doesn''t fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set.

NSStringDrawingUsesLineFragmentOrigin = 1 << 0, // The specified origin is the line fragment origin, not the base line origin

NSStringDrawingUsesFontLeading = 1 << 1, // Uses the font leading for calculating line heights

NSStringDrawingUsesDeviceMetrics = 1 << 3, // Uses image glyph bounds instead of typographic bounds

} NS_ENUM_AVAILABLE_IOS(6_0);

NSStringDrawingTruncatesLastVisibleLine:

如果文本内容超出指定的矩形限制,文本将被截去并在最后一个字符后加上省略号。如果没有指定NSStringDrawingUsesLineFragmentOrigin选项,则该选项被忽略。

NSStringDrawingUsesLineFragmentOrigin:

绘制文本时使用 line fragement origin 而不是 baseline origin。

The origin specified when drawing the string is the line fragment origin and not the baseline origin.

NSStringDrawingUsesFontLeading:

计算行高时使用行距。(译者注:字体大小+行间距=行距)

NSStringDrawingUsesDeviceMetrics:

计算布局时使用图元字形(而不是印刷字体)。

Use the image glyph bounds (instead of the typographic bounds) when computing layout.

简单写了一个Demo来看看该方法的使用,并比较了一下各个options的不同,首先是代码:

NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:textView.text];

textView.attributedText = attrStr;

NSRange range = NSMakeRange(0, attrStr.length);

NSDictionary *dic = [attrStr attributesAtIndex:0 effectiveRange:&range];   // 获取该段attributedString的属性字典

// 计算文本的大小

CGSize textSize = [textView.text boundingRectWithSize:textView.bounds.size // 用于计算文本绘制时占据的矩形块

options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading // 文本绘制时的附加选项

attributes:dic        // 文字的属性

context:nil].size; // context上下文。包括一些信息,例如如何调整字间距以及缩放。该对象包含的信息将用于文本绘制。该参数可为nil

NSLog(@"w = %f", textSize.width);

NSLog(@"h = %f", textSize.height);

再看看不同的options下控制台的输出结果:

NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading

2013-09-02 21:04:47.470 BoudingRect_i7_Demo[3532:a0b] w = 322.171875

2013-09-02 21:04:47.471 BoudingRect_i7_Demo[3532:a0b] h = 138.000015

NSStringDrawingUsesLineFragmentOrigin // The specified origin is the line fragment origin, not the base line origin

2013-09-02 17:35:40.547 BoudingRect_i7_Demo[1871:a0b] w = 318.398438

2013-09-02 17:35:40.549 BoudingRect_i7_Demo[1871:a0b] h = 69.000000

NSStringDrawingTruncatesLastVisibleLine // Truncates and adds the ellipsis character to the last visible line if the text doesn''t fit into the bounds specified. Ignored if NSStringDrawingUsesLineFragmentOrigin is not also set.

2013-09-02 17:37:38.398 BoudingRect_i7_Demo[1902:a0b] w = 1523.408203

2013-09-02 17:37:38.400 BoudingRect_i7_Demo[1902:a0b] h = 13.800000

NSStringDrawingUsesFontLeading // Uses the font leading for calculating line heights

2013-09-02 17:40:45.903 BoudingRect_i7_Demo[1932:a0b] w = 1523.408203

2013-09-02 17:40:45.905 BoudingRect_i7_Demo[1932:a0b] h = 13.800000

NSStringDrawingUsesDeviceMetrics // Uses image glyph bounds instead of typographic bounds

2013-09-02 17:42:03.283 BoudingRect_i7_Demo[1956:a0b] w = 1523.408203

2013-09-02 17:42:03.284 BoudingRect_i7_Demo[1956:a0b] h = 13.800000

其 中如果options参数为NSStringDrawingUsesLineFragmentOrigin,那么整个文本将以每行组成的矩形为单位计算整 个文本的尺寸。(在这里有点奇怪,因为字体高度大概是13.8,textView中大概有10行文字,此时用该选项计算出来的只有5行,即高度为69,而 同时使用NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin却可以得出文字刚好有10行,即高度为138,这里要等iOS7官方的文档 出来再看看选项的说明,因为毕竟以上文档是iOS6的东西)

如果为NSStringDrawingTruncatesLastVisibleLine或者NSStringDrawingUsesDeviceMetric,那么计算文本尺寸时将以每个字或字形为单位来计算。

如果为NSStringDrawingUsesFontLeading则以字体间的行距(leading,行距:从一行文字的底部到另一行文字底部的间距。)来计算。

各个参数是可以组合使用的,如NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine。

根据该方法我调整了一下Reader的分页方法:(主要是将被iOS7 Deprecated的sizeWithFont:constrainedToSize:lineBreakMode:方法改成了 boudingRectWithSize:options:attributes:context:方法来计算文本尺寸)

/* 判断是否需要分页和进行分页动作 */

-(BOOL)paging

{

/* 获取Settings中设定好的字体(主要是获取字体大小) */

static const CGFloat textScaleFactor = 1.; // 设置文字比例

NSString *textStyle = [curPageView.textView tkd_textStyle]; // 设置文字样式

preferredFont_ = [UIFont tkd_preferredFontWithTextStyle:textStyle scale:textScaleFactor]; //设置prferredFont(包括样式和大小)

NSLog(@"paging: %@", preferredFont_.fontDescriptor.fontAttributes); // 在控制台中输出字体的属性字典

/* 设定每页的页面尺寸 */

NSUInteger height = (int)self.view.bounds.size.height - 40.0; // 页面的高度

/* 获取文本的总尺寸 */

NSDictionary *dic = preferredFont_.fontDescriptor.fontAttributes;

CGSize totalTextSize = [bookItem.content.string boundingRectWithSize:curPageView.textView.bounds.size

options:NSStringDrawingUsesLineFragmentOrigin

attributes:dic

context:nil].size;

NSLog(@"w = %f", totalTextSize.width);

NSLog(@"h = %f", totalTextSize.height);

/* 开始分页 */

if (totalTextSize.height < height) {

/* 如果一页就能显示完,直接显示所有文本 */

totalPages_   = 1;             // 设定总页数为1

charsPerPage_ = [bookItem.content length]; // 设定每页的字符数

textLength_   = [bookItem.content length]; // 设定文本总长度

return NO;                     // 不用分页

}

else {

/* 计算理想状态下的页面数量和每页所显示的字符数量,用来作为参考值用 */

textLength_                       = [bookItem.content length];                   // 文本的总长度

NSUInteger referTotalPages        = (int)totalTextSize.height / (int)height + 1; // 理想状态下的总页数

NSUInteger referCharactersPerPage = textLength_ / referTotalPages;               // 理想状态下每页的字符数

// 输出理想状态下的参数信息

NSLog(@"textLength             = %d", textLength_);

NSLog(@"referTotalPages        = %d", referTotalPages);

NSLog(@"referCharactersPerPage = %d", referCharactersPerPage);

/* 根据referCharactersPerPage和text view的高度开始动态调整每页的字符数 */

// 如果referCharactersPerPage过大,则直接调整至下限值,减少调整的时间

if (referCharactersPerPage > 1000) {

referCharactersPerPage = 1000;

}

// 获取理想状态下的每页文本的范围和pageText及其尺寸

NSRange range       = NSMakeRange(referCharactersPerPage, referCharactersPerPage); // 一般第一页字符数较少,所以取第二页的文本范围作为调整的参考标准

NSString *pageText  = [bookItem.content.string substringWithRange:range]; // 获取该范围内的文本

NSLog(@"%@", pageText);

NSRange ptrange = NSMakeRange(0, pageText.length);

NSDictionary *ptdic = [[bookItem.content attributedSubstringFromRange:ptrange] attributesAtIndex:0 effectiveRange:&ptrange];

CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size

options:NSStringDrawingUsesLineFragmentOrigin

attributes:ptdic

context:nil].size;

// 若pageText超出text view的显示范围,则调整referCharactersPerPage

NSLog(@"height = %d", height);

while (pageTextSize.height > height) {

NSLog(@"pageTextSize.height = %f", pageTextSize.height);

referCharactersPerPage -= 2;                                      // 每页字符数减2

range                   = NSMakeRange(0, referCharactersPerPage); // 重置每页字符的范围

ptdic = [[bookItem.content attributedSubstringFromRange:range] attributesAtIndex:0 effectiveRange:&range];

CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size

options:NSStringDrawingUsesLineFragmentOrigin

attributes:ptdic

context:nil].size;

pageText                = [bookItem.content.string substringWithRange:range];        // 重置pageText

pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size

options:NSStringDrawingUsesLineFragmentOrigin

attributes:ptdic

context:nil].size; // 获取pageText的尺寸

}

// 根据调整后的referCharactersPerPage设定好charsPerPage_

charsPerPage_ = referCharactersPerPage;

NSLog(@"cpp: %d", charsPerPage_);

// 计算totalPages_

totalPages_ = (int)bookItem.content.length / charsPerPage_ + 1;

NSLog(@"ttp: %d", totalPages_);

// 计算最后一页的字符数,防止范围溢出

charsOfLastPage_ = textLength_ - (totalPages_ - 1) * charsPerPage_;

NSLog(@"colp: %d", charsOfLastPage_);

// 分页完成

return YES;

}

}

这样就看不到碍眼的黄色警告标志了。

重要的是,由于该方法计算文本的尺寸更为准确,所以可以使得分页后页与页之间的连贯性好了很多,而且每页的空间利用率都提高了很多,每页的文字几乎铺满了整个页面。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容