iOS 开发iOS 技术iOS开发实用技术

动态计算NSAttributedString的size大小

2016-04-20  本文已影响9187人  lele8446

NSAttributedString(富文本),作为NSString的子类,是一种带有属性的字符串,通过它可以轻松的在一个字符串中表现出多种字体、字号、背景色、下划线等各不相同的风格,还可以对段落进行格式化。下面就来探讨一下动态计算NSAttributedString的size大小实现:


使用boundingRectWithSize:options:attributes:context计算

系统提供了- boundingRectWithSize:options:attributes:context:方法来计算NSAttributedString的size大小,- sizeWithFont:constrainedToSize:lineBreakMode:已经被废弃了。

  /**
   *  return 动态返回字符串size大小
   *
   *  @param aString 字符串
   *  @param width   指定宽度
   *  @param height  指定宽度
   *
   *  @return CGSize
   */
  + (CGSize)getStringRect:(NSAttributedString *)aString width:(CGFloat)width height:(CGFloat)height
  {
     CGSize size = CGSizeZero;
     NSMutableAttributedString *atrString = [[NSMutableAttributedString alloc] initWithAttributedString:aString];
     NSRange range = NSMakeRange(0, atrString.length);

     //获取指定位置上的属性信息,并返回与指定位置属性相同并且连续的字符串的范围信息。
     NSDictionary* dic = [atrString attributesAtIndex:0 effectiveRange:&range];
     //不存在段落属性,则存入默认值
     NSMutableParagraphStyle *paragraphStyle = dic[NSParagraphStyleAttributeName];
     if (!paragraphStyle || nil == paragraphStyle) {
          paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
          paragraphStyle.lineSpacing = 0.0;//增加行高
          paragraphStyle.headIndent = 0;//头部缩进,相当于左padding
          paragraphStyle.tailIndent = 0;//相当于右padding
          paragraphStyle.lineHeightMultiple = 0;//行间距是多少倍
          paragraphStyle.alignment = NSTextAlignmentLeft;//对齐方式
          paragraphStyle.firstLineHeadIndent = 0;//首行头缩进
          paragraphStyle.paragraphSpacing = 0;//段落后面的间距
          paragraphStyle.paragraphSpacingBefore = 0;//段落之前的间距
          [atrString addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:range];
      }

     //设置默认字体属性
     UIFont *font = dic[NSFontAttributeName];
     if (!font || nil == font) {
          font = [UIFont fontWithName:@"HelveticaNeue" size:12.0];
          [atrString addAttribute:NSFontAttributeName value:font range:range];
      }

     NSMutableDictionary *attDic = [NSMutableDictionary dictionaryWithDictionary:dic];
     [attDic setObject:font forKey:NSFontAttributeName];
     [attDic setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];

     CGSize strSize = [[aString string] boundingRectWithSize:CGSizeMake(width, height)
                                                options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                             attributes:attDic
                                                context:nil].size;

     size = CGSizeMake(CGFloat_ceil(strSize.width), CGFloat_ceil(strSize.height));
     return size;
  }

需要注意的是调用时,要选择NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading选项,不然计算出来的高度不准确

通过sizeToFit计算
  /**
   *  返回UILabel自适应后的size
   *
   *  @param aString 字符串
   *  @param width   指定宽度
   *  @param height  指定高度
   *
   *  @return CGSize
   */
  + (CGSize)sizeLabelToFit:(NSAttributedString *)aString width:(CGFloat)width height:(CGFloat)height {
     UILabel *tempLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, width, height)];
     tempLabel.attributedText = aString;
     tempLabel.numberOfLines = 0;
     [tempLabel sizeToFit];
     CGSize size = tempLabel.frame.size;
     size = CGSizeMake(CGFloat_ceil(size.width), CGFloat_ceil(size.height));
     return size;
  }

其实就是通过新建一个临时的UILabel,然后通过sizeToFit方法计算出合适的CGSize。

通过CTFramesetter进行计算
  1. Bounding Box(边界框 bbox),这是一个假想的框子,它尽可能紧密的装入字形。
  2. Baseline(基线),一条假想的线,一行上的字形都以此线作为上下位置的参考,在这条线的左侧存在一个点叫做基线的原点。
  3. Ascent(上行高度)从原点到字体中最高(这里的高深都是以基线为参照线的)的字形的顶部的距离,Ascent是一个正值。
  4. Descent(下行高度)从原点到字体中最深的字形底部的距离,Descent是一个负值(比如一个字体原点到最深的字形的底部的距离为2,那么Descent就为-2)。
  5. Linegap(行距),Linegap也可以称作leading(其实准确点讲应该叫做External leading),行高LineHeight则可以通过 Ascent + |Descent| + Linegap 来计算。
  6. Origin(每一行的原点),Origin是在图中的baseLine处的。
上一篇下一篇

猜你喜欢

热点阅读