UILabel自适应宽度的函数详解
之前用Text Kit写Reader的时候,在分页时要计算一段文本的尺寸大小,之前使用了NSString类的sizeWithFont:constrainedToSize:lineBreakMode:方法,但是该方法已经被iOS7 Deprecated了,而iOS7新出了一个boudingRectWithSize:options:attributes:context方法来代替:
很碍眼的黄色警告标志。
先来看看iOS7 SDK包中关于
boudingRectWithSize:options:attributes:context
方法的定义:

// NOTE: All of the following methods will default to drawing on a baseline, limiting drawing to a single line.
// To correctly draw and size multi-line text, pass NSStringDrawingUsesLineFragmentOrigin in the options parameter.
@interface NSString (NSExtendedStringDrawing)
- (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0);
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0);
@end
关于该方法,NSAttributedString其实也有一个同名的方法:
@interface NSAttributedString (NSExtendedStringDrawing)
- (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);
- (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);
@end
该方法在iOS6就可以使用了。
关于该类,有一篇关于NSAttributedString UIKit Additions Reference翻译的文章:http://blog.csdn.net/kmyhy/article/details/8895643
里面就说到了该方法:
boundingRectWithSize:options:context:
返回文本绘制所占据的矩形空间。
- (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;
UILabel自适应宽度的函数详解的更多相关文章
- 【C语言】printf函数详解
C语言printf函数详解 一.相关基础知识 请求printf()打印变量的指令取决于变量的类型,例如打印整数用%d符号,打印字符用%c符号,这些符号称为转换说明(conversion specifi ...
- 实例-sprintf() 函数详解-输出格式转换函数
Part1:实例 $filterfile = basename(PHP_SELF, '.php'); if (isset($_GET['uselastfilter']) && isse ...
- 【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解
原文:3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解 3D中z值会影响屏幕坐标系到世界坐标系之间的转换,2D中Z值不会产生影响(而只 ...
- malloc 与 free函数详解<转载>
malloc和free函数详解 本文介绍malloc和free函数的内容. 在C中,对内存的管理是相当重要.下面开始介绍这两个函数: 一.malloc()和free()的基本概念以及基本用法: 1 ...
- NSSearchPathForDirectoriesInDomains函数详解
NSSearchPathForDirectoriesInDomains函数详解 #import "NSString+FilePath.h" @implementation ...
- JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解
二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...
- Linux C popen()函数详解
表头文件 #include<stdio.h> 定义函数 FILE * popen( const char * command,const char * type); 函数说明 popen( ...
- kzalloc 函数详解(转载)
用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0. view plain /** * kzal ...
- Netsuite Formula > Oracle函数列表速查(PL/SQL单行函数和组函数详解).txt
PL/SQL单行函数和组函数详解 函数是一种有零个或多个参数并且有一个返回值的程序.在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句,函数主要分为两大类: 单行函数 ...
随机推荐
- Windows环境配置Apache+Mysql+PHP
一.安装配置Apache2.4.7(httpd-2.4.7-win64-VC11.zip ) 1.解压下载的安装包:httpd-2.4.7-win64-VC11.zip将其放到自己的安装目录(我的目录 ...
- Loadrunner基础:Loadrunner Vuser基本概念和应用
学习示例 Loadrunner自带有WebTour的网站可以帮助初学者学习性能测试安装完Loadrunner以后进入到Program Files下的WebTour文件加,启动WebTour服务在浏览器 ...
- (19)odoo中的javascript
-----------更新日期15:17 2016-02-16 星期二-----------* 用到的js库 我们可以打开 addons/web/views/webclient_template. ...
- C语言中static变量详解
Static翻译出来是“静态”“静止”的意思,在C语言中的意思其实和它的本意差不多,表示“静态”或者“全局”的意思,用来修饰变量和函数.经static修饰过后的变量或者函数的作用域或者存储域会发生变化 ...
- hdu5884 Sort(二分+k叉哈夫曼树)
题目链接:hdu5884 Sort 题意:n个有序序列的归并排序.每次可以选择不超过k个序列进行合并,合并代价为这些序列的长度和.总的合并代价不能超过T, 问k最小是多少. 题解:先二分k,然后在k给 ...
- Asp.Net 导出Excel数据文件
表格例子如下: <table id="tableExcel" width="100%" border="1" cellspacing= ...
- 如何获得bin/Debug目录的路径?
这个可以使用下面的方法 答案:bin目录:通过应用程序集合AppDomain.CurrentDomain.BaseDirectory:带有"\"System.Environment ...
- C#语法小用法
数据在存为数据库之前,用JS的encodeURIComponent进行编码,现需要在后台代码中进行解码,实现decodeURIComponent的功能, 如下: HttpUtility.UrlDeco ...
- 转载——PLSQL developer 连接不上64位Oracle 解决办法
前两天刚下载了oracle 11g 64位的最新版本,安装成功之后,再安装PLSQL.结果使用PLSQL访问数据库时,死活连接不上.报错如下: Could not load "……\bin\ ...
- java 面向对象编程-- 第十三章 反射、类加载与垃圾回收
1.狭义JavaBean规范 Javabean必须包含一个无参数的public构造方法,方便通过反射的方式产生对象. 属性必须都是私有的. Javabean必须包含符合命名规范的get和set方法,以 ...