UILabel自适应宽度的函数详解

之前用Text Kit写Reader的时候,在分页时要计算一段文本的尺寸大小,之前使用了NSString类的sizeWithFont:constrainedToSize:lineBreakMode:方法,但是该方法已经被iOS7 Deprecated了,而iOS7新出了一个boudingRectWithSize:options:attributes:context方法来代替:

很碍眼的黄色警告标志。

先来看看iOS7 SDK包中关于

boudingRectWithSize:options:attributes:context

方法的定义:

  1. // 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其实也有一个同名的方法:

  1. @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

  1. 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的不同,首先是代码:

  1. 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下控制台的输出结果:

  1. 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:方法来计算文本尺寸)

    1. /* 判断是否需要分页和进行分页动作 */
      -(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;   

时间: 2024-08-07 02:06:40

UILabel自适应宽度的函数详解的相关文章

delphi中的Format函数详解

首先看它的声明:[[email protected]][@21ki!] function Format(const Format: string; const Args: array of const): string; overload;[[email protected]][@21ki!] 事实上Format方法有两种形式,另外一种是三个参数的,主要区别在于它是线程安全的,[[email protected]][@21ki!]但并不多用,所以这里只对第一个介绍:[[email protect

【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解

原文:3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解 3D中z值会影响屏幕坐标系到世界坐标系之间的转换,2D中Z值不会产生影响(而只是屏幕宽高比会产生影响,z值只对深度剔除产生影响).所以U3D中如果用2D摄像机那么屏幕坐标和世界坐标之间的转换需要用指定的2D摄像机才行,如果用主3D摄像机那么UI转换会产生计算结果异常. 一.D3DXMatrixPerspectiveFovLH函数 作用:Builds a left-handed

windows API中CreateWindow()函数详解

CreateWindow函数详解 在注册完窗口类后就需要进行窗口的创建,用到的函数理所当然就是CreateWindow(), 而这个函数是基于窗口类的,所以还需要指定几个参数来制定特定的窗口.而且像一些不带边框的窗口是怎么创建的也是具有相当的技巧的,就是创建的是不带标题和边框的窗口,然后自己在客户区绘制程序的内容,能够制作个性化的应用程序. API解释 该函数创建一个重叠式窗口.弹出式窗口或子窗口.它指定窗口类,窗口标题,窗口风格,以及窗口的初始位置及大小(可选的).函数也指该窗口的父窗口或所属

php socket函数详解

转自:http://blog.163.com/[email protected]/blog/static/2889641420138213514298/ 最近在用socket实现服务端向客户端主动推送消息函数名 描述socket_accept() 接受一个Socket连接socket_bind() 把socket绑定在一个IP地址和端口上socket_clear_error() 清除socket的错误或最后的错误代码socket_close() 关闭一个socket资源socket_connec

c++ 虚函数详解

下面是对C++的虚函数的理解. 一,定义 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略.下面来看一段简单的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 classA { publi

linux网络编程之shutdown() 与 close()函数详解

linux网络编程之shutdown() 与 close()函数详解 参考TCPIP网络编程和UNP: shutdown函数不能关闭套接字,只能关闭输入和输出流,然后发送EOF,假设套接字为A,那么这个函数会关闭所有和A相关的套接字,包括复制的:而close能直接关闭套接字. 1.close()函数 [cpp] view plain copy print? <span style="font-size:13px;">#include<unistd.h> int 

linux中fork()函数详解[zz]

转载自:http://www.cnblogs.com/york-hust/archive/2012/11/23/2784534.html 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事. 一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有

Python内置函数详解

置顶   内置函数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii 此文参考了别人整理好的东西(地址:http://www.cnblogs.com/sesshoumaru/p/6140987.html#p1),然后结合自己的理解,写下来,一方面方便自己,让自己好好学习,顺便回忆回忆:另一方面,让喜欢的盆友也参考一下. 经查询,3.6版本总共有68个内置函数,主要分类如下: 数学运算(7个) 类型转换

CreateFile函数详解

CreateFile函数详解 CreateFile The CreateFile function creates or opens the following objects and returns a handle that can be used to accessthe object: files pipes mailslots communications resources disk devices(Windows NT only) consoles directories(open