Alvin's Blog

NSAttributedString 计算高度的天坑...

苹果对富文本的支持越来越完备, 在iOS7中更好地支持HTML了,现在你可以用NSAttributedString来展示HTML的内容.

尝试使用NSHTMLTextDocumentType展示HTML吧:

1
2
3
4
5
6
7
NSString *html = @"<bold>Wow!</bold> Now <em>iOS</em> can create <h3>NSAttributedString</h3> from HTMLs!";
NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};

NSAttributedString *attrString = [[NSAttributedString alloc] initWithData:[html dataUsingEncoding:NSUTF8StringEncoding]
                                                                  options:options
                                                       documentAttributes:nil
                                                                    error:nil];

同样你也可以通过NSAttributedString获得HTML文本:

1
2
3
4
5
NSAttributedString *attrString; // from previous code
NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};

NSData *htmlData = [attrString dataFromRange:NSMakeRange(0, [attrString length]) documentAttributes:options error:nil];
NSString *htmlString = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];

但是再享受NSAttributedString带来方便的同时,我们也会遇到些令人头痛的小麻烦…

比如在计算高度的时候我就遇到了个蛋疼的问题:

这里介绍下计算高度的方法…需要的同学拿去吧~ 或者你也可以从Mattt大神哪里获得一个更牛逼的计算方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface NSString (Calculate)

- (CGSize)sizeWithAttributes:(NSDictionary *)attrs constrainedToSize:(CGSize)size;

@end

@implementation NSString (Calculate)

- (CGSize)sizeWithAttributes:(NSDictionary *)attrs constrainedToSize:(CGSize)size
{
  NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:self
                                                                         attributes:attrs];
  CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attributedString);
  CGSize fitSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), NULL, size, NULL);
  CFRelease(framesetter);
  return fitSize;
}

@end

故事到这里看似都顺风顺水

1
2
3
4
5
6
  NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
  paragraphStyle.lineSpacing = 3;
  paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;

  NSDictionary *attributes = @{NSFontAttributeName : [UIFont systemFontOfSize:12],
                               NSParagraphStyleAttributeName : paragraphStyle}

直到我将NSMutableParagraphStyle的lineBreakMode设置成NSLineBreakByTruncatingTail之后,就开始蛋疼了… 这个时候返回的高度永远都只是单行的高度…

百般寻觅之后终于知道了问题所在:

lineBreakMode 设置成 NSLineBreakByTruncatingTail | NSLineBreakByTruncatingHead | NSLineBreakByTruncatingMiddle 在计算高度的时候 会被系统默认成单行

所以这里需要为计算高度的时候单独设置一个paragraphStyle,不用担心,在这里计算得到高度与最后显示的结果是一致的…

1
2
3
4
5
6
7
8
9
10
+ (NSDictionary *)attributesForUserTextWithCalculateMode:(BOOL)isCalculateMode
{
  NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
  paragraphStyle.lineSpacing = 3;
  paragraphStyle.lineBreakMode = isCalculateMode ? NSLineBreakByWordWrapping : NSLineBreakByTruncatingTail;

  return @{NSFontAttributeName : [UIFont systemFontOfSize:12],
           NSParagraphStyleAttributeName : paragraphStyle,
           NSForegroundColorAttributeName: [UIColor blackColor]};
}

木哈哈…这样看起来好多了≖‿≖✧

参考链接:

http://lists.apple.com/archives/cocoa-dev/2012/Nov/msg00179.html http://stackoverflow.com/questions/7378308/uibuttons-title-label-word-wrap-with-tail-truncation