zoukankan      html  css  js  c++  java
  • TextKit学习(四)通过boundingRectWithSize:options:attributes:context:计算文本尺寸

    from:http://blog.csdn.net/u010962810/article/details/10949279
    之前用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.  
    2. // To correctly draw and size multi-line text, pass NSStringDrawingUsesLineFragmentOrigin in the options parameter.  
    3. @interface NSString (NSExtendedStringDrawing)  
    4. - (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0);  
    5. - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options attributes:(NSDictionary *)attributes context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(7_0);  
    6. @end  

    关于该方法,NSAttributedString其实也有一个同名的方法:

    1. @interface NSAttributedString (NSExtendedStringDrawing)  
    2. - (void)drawWithRect:(CGRect)rect options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);  
    3. - (CGRect)boundingRectWithSize:(CGSize)size options:(NSStringDrawingOptions)options context:(NSStringDrawingContext *)context NS_AVAILABLE_IOS(6_0);  
    4. @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) {  
    2.     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.  
    3.     NSStringDrawingUsesLineFragmentOrigin = 1 << 0, // The specified origin is the line fragment origin, not the base line origin  
    4.     NSStringDrawingUsesFontLeading = 1 << 1, // Uses the font leading for calculating line heights  
    5.     NSStringDrawingUsesDeviceMetrics = 1 << 3, // Uses image glyph bounds instead of typographic bounds  
    6. } 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];  
    2. textView.attributedText = attrStr;  
    3. NSRange range = NSMakeRange(0, attrStr.length);  
    4. NSDictionary *dic = [attrStr attributesAtIndex:0 effectiveRange:&range];   // 获取该段attributedString的属性字典  
    5. // 计算文本的大小  
    6. CGSize textSize = [textView.text boundingRectWithSize:textView.bounds.size // 用于计算文本绘制时占据的矩形块  
    7.                                               options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading // 文本绘制时的附加选项  
    8.                                            attributes:dic        // 文字的属性  
    9.                                               context:nil].size; // context上下文。包括一些信息,例如如何调整字间距以及缩放。该对象包含的信息将用于文本绘制。该参数可为nil  
    10. NSLog(@"w = %f", textSize.width);  
    11. NSLog(@"h = %f", textSize.height);  
    再看看不同的options下控制台的输出结果:

    1. NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading  
    2. 2013-09-02 21:04:47.470 BoudingRect_i7_Demo[3532:a0b] w = 322.171875  
    3. 2013-09-02 21:04:47.471 BoudingRect_i7_Demo[3532:a0b] h = 138.000015  
    4.   
    5. NSStringDrawingUsesLineFragmentOrigin // The specified origin is the line fragment origin, not the base line origin  
    6. 2013-09-02 17:35:40.547 BoudingRect_i7_Demo[1871:a0b] w = 318.398438  
    7. 2013-09-02 17:35:40.549 BoudingRect_i7_Demo[1871:a0b] h = 69.000000  
    8.   
    9. 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.  
    10. 2013-09-02 17:37:38.398 BoudingRect_i7_Demo[1902:a0b] w = 1523.408203  
    11. 2013-09-02 17:37:38.400 BoudingRect_i7_Demo[1902:a0b] h = 13.800000  
    12.   
    13. NSStringDrawingUsesFontLeading // Uses the font leading for calculating line heights  
    14. 2013-09-02 17:40:45.903 BoudingRect_i7_Demo[1932:a0b] w = 1523.408203  
    15. 2013-09-02 17:40:45.905 BoudingRect_i7_Demo[1932:a0b] h = 13.800000  
    16.   
    17. NSStringDrawingUsesDeviceMetrics // Uses image glyph bounds instead of typographic bounds  
    18. 2013-09-02 17:42:03.283 BoudingRect_i7_Demo[1956:a0b] w = 1523.408203  
    19. 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. /* 判断是否需要分页和进行分页动作 */  
    2. -(BOOL)paging  
    3. {  
    4.     /* 获取Settings中设定好的字体(主要是获取字体大小) */  
    5.     static const CGFloat textScaleFactor = 1.; // 设置文字比例  
    6.     NSString *textStyle = [curPageView.textView tkd_textStyle]; // 设置文字样式  
    7.     preferredFont_ = [UIFont tkd_preferredFontWithTextStyle:textStyle scale:textScaleFactor]; //设置prferredFont(包括样式和大小)  
    8.     NSLog(@"paging: %@", preferredFont_.fontDescriptor.fontAttributes); // 在控制台中输出字体的属性字典  
    9.       
    10.       
    11.     /* 设定每页的页面尺寸 */  
    12.     NSUInteger height = (int)self.view.bounds.size.height - 40.0; // 页面的高度  
    13.       
    14.       
    15.     /* 获取文本的总尺寸 */  
    16.     NSDictionary *dic = preferredFont_.fontDescriptor.fontAttributes;  
    17.     CGSize totalTextSize = [bookItem.content.string boundingRectWithSize:curPageView.textView.bounds.size  
    18.                                                                  options:NSStringDrawingUsesLineFragmentOrigin  
    19.                                                               attributes:dic  
    20.                                                                  context:nil].size;  
    21.     NSLog(@"w = %f", totalTextSize.width);  
    22.     NSLog(@"h = %f", totalTextSize.height);  
    23.       
    24.       
    25.     /* 开始分页 */  
    26.     if (totalTextSize.height < height) {  
    27.         /* 如果一页就能显示完,直接显示所有文本 */  
    28.         totalPages_   = 1;             // 设定总页数为1  
    29.         charsPerPage_ = [bookItem.content length]; // 设定每页的字符数  
    30.         textLength_   = [bookItem.content length]; // 设定文本总长度  
    31.         return NO;                     // 不用分页  
    32.     }  
    33.     else {  
    34.         /* 计算理想状态下的页面数量和每页所显示的字符数量,用来作为参考值用 */  
    35.         textLength_                       = [bookItem.content length];                   // 文本的总长度  
    36.         NSUInteger referTotalPages        = (int)totalTextSize.height / (int)height + 1; // 理想状态下的总页数  
    37.         NSUInteger referCharactersPerPage = textLength_ / referTotalPages;               // 理想状态下每页的字符数  
    38.         // 输出理想状态下的参数信息  
    39.         NSLog(@"textLength             = %d", textLength_);  
    40.         NSLog(@"referTotalPages        = %d", referTotalPages);  
    41.         NSLog(@"referCharactersPerPage = %d", referCharactersPerPage);  
    42.           
    43.           
    44.         /* 根据referCharactersPerPage和text view的高度开始动态调整每页的字符数 */  
    45.         // 如果referCharactersPerPage过大,则直接调整至下限值,减少调整的时间  
    46.         if (referCharactersPerPage > 1000) {  
    47.             referCharactersPerPage = 1000;  
    48.         }  
    49.           
    50.         // 获取理想状态下的每页文本的范围和pageText及其尺寸  
    51.         NSRange range       = NSMakeRange(referCharactersPerPage, referCharactersPerPage); // 一般第一页字符数较少,所以取第二页的文本范围作为调整的参考标准  
    52.         NSString *pageText  = [bookItem.content.string substringWithRange:range]; // 获取该范围内的文本  
    53.         NSLog(@"%@", pageText);  
    54.           
    55.           
    56.         NSRange ptrange = NSMakeRange(0, pageText.length);  
    57.         NSDictionary *ptdic = [[bookItem.content attributedSubstringFromRange:ptrange] attributesAtIndex:0 effectiveRange:&ptrange];  
    58.         CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size  
    59.                                                      options:NSStringDrawingUsesLineFragmentOrigin  
    60.                                                   attributes:ptdic  
    61.                                                      context:nil].size;  
    62.           
    63.         // 若pageText超出text view的显示范围,则调整referCharactersPerPage  
    64.         NSLog(@"height = %d", height);  
    65.         while (pageTextSize.height > height) {   
    66.             NSLog(@"pageTextSize.height = %f", pageTextSize.height);  
    67.             referCharactersPerPage -= 2;                                      // 每页字符数减2  
    68.             range                   = NSMakeRange(0, referCharactersPerPage); // 重置每页字符的范围  
    69.             ptdic = [[bookItem.content attributedSubstringFromRange:range] attributesAtIndex:0 effectiveRange:&range];  
    70.             CGSize pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size  
    71.                                                          options:NSStringDrawingUsesLineFragmentOrigin  
    72.                                                       attributes:ptdic  
    73.                                                          context:nil].size;  
    74.             pageText                = [bookItem.content.string substringWithRange:range];        // 重置pageText  
    75.               
    76.             pageTextSize = [pageText boundingRectWithSize:curPageView.textView.bounds.size  
    77.                                                   options:NSStringDrawingUsesLineFragmentOrigin  
    78.                                                attributes:ptdic  
    79.                                                   context:nil].size; // 获取pageText的尺寸  
    80.         }  
    81.           
    82.         // 根据调整后的referCharactersPerPage设定好charsPerPage_  
    83.         charsPerPage_ = referCharactersPerPage;   
    84.         NSLog(@"cpp: %d", charsPerPage_);  
    85.           
    86.         // 计算totalPages_  
    87.         totalPages_ = (int)bookItem.content.length / charsPerPage_ + 1;  
    88.         NSLog(@"ttp: %d", totalPages_);  
    89.           
    90.         // 计算最后一页的字符数,防止范围溢出  
    91.         charsOfLastPage_ = textLength_ - (totalPages_ - 1) * charsPerPage_;  
    92.         NSLog(@"colp: %d", charsOfLastPage_);  
    93.           
    94.         // 分页完成  
    95.         return YES;  
    96.     }  
    97. }  
    这样就看不到碍眼的黄色警告标志了。



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




     


  • 相关阅读:
    Atom,AtomPub 和Java 转载
    使用OData协议查询Windows日志 转
    许可协议BSD GPL MPL LGPL APL转载
    Eclipse Galileo 简介
    常见证书格式和转换
    JVM简介转
    Android Native 代码开发学习笔记转载
    echarts——各个配置项详细说明总结
    Mysql 安装服务无法启动解决方案与使用的一般使用指令
    js中如何把字符串(文本)转化为对象
  • 原文地址:https://www.cnblogs.com/zsw-1993/p/4879676.html
Copyright © 2011-2022 走看看