含有Emoji表情的文本计算高度

brighttang 7年前
   <p style="text-align:center"><img src="https://simg.open-open.com/show/2d7cdb3b7c927e729b978d1ce86858aa.png"></p>    <p>最进开发社区时遇到一个需求,产品那边的需求是要做成和QQ空间一样的评论功能,在评论内容中要显示昵称,并且点击昵称要进入个人主页。查了好多资料,看了YYText和TTTAttributedLabel的官方文章,也没找到结果,最后自己抱着试一试的态度,结果还真的实现了需求,记录下来,分享给大家,希望对你们会有所帮助</p>    <p>先来一张效果图</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/23afaf5f597b01807115e196141063eb.png"></p>    <p>这个是最后的效果图,点击浅蓝色文字会跳到个人主页,不过还有点小问题,文字的间距还需要微调下。不过思路已经有了。</p>    <p>先说下自己遇到的问题。点击文字链接用的是YYText,实现起来很简单,但是问题是,当字符串中含有Emoji表情时,行间距就出现的了问题。本来我们需要的是这样的。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/42bff918e88b13e67871789bfb08fa80.png"></p>    <p>但是使用YYText或者TTTAttributedLabel后变成了这样</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/45a5ab7a871f32bb0b13b2484381a682.png"></p>    <p>间距明显不一样</p>    <p>首先说明下,对于含有Emoji表情的字符串,使用UILabel来展示没有任何问题,间距也都是一样的,但是使用YYText或者TTTAttributedLabel来添加文字的点击事件之后都会变成含有Emoji表情的行高比正常文字的行高要高。</p>    <p>本以为YYText或者TTTAttributedLabel本身有方法可以设置,很遗憾,我没找到(如果你知道的话,请留言告诉我),无奈之下只能使用比较笨的方法。</p>    <p>先说下我的思路:目前的结果是含有Emoji表情的行高要比预期的高,于是就想到,能不能获取每行的内容,然后循环遍历每行的文字,判断是否含友Emoji表情,如果有则不做处理,如果没有这设置行高和含有Emoji表情的行高一样的高度,问题不就解决了吗。</p>    <p>第一步:先要获取,每行的文字</p>    <pre>  <code class="language-objectivec">/**   *  获取每行的文字   *   *  @param text     文本内容   *  @param font     字体   *  @param maxWidth 容器的最大宽度   *   *  @return 存储每行文字的数组   */    - (NSArray *)getSeparatedLinesFromtext:(NSString *)text font:(UIFont *)font maxWidth:(CGFloat)maxWidth  {      CTFontRef myFont = CTFontCreateWithName((__bridge CFStringRef)([font fontName]), [font pointSize], NULL);      NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:text];      [attStr addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)myFont range:NSMakeRange(0, attStr.length)];        CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)attStr);        CGMutablePathRef path = CGPathCreateMutable();      CGPathAddRect(path, NULL, CGRectMake(0,0,maxWidth,100000));        CTFrameRef frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, NULL);        NSArray *lines = (__bridge NSArray *)CTFrameGetLines(frame);      NSMutableArray *linesArray = [[NSMutableArray alloc]init];        for (id line in lines)      {          CTLineRef lineRef = (__bridge CTLineRef )line;          CFRange lineRange = CTLineGetStringRange(lineRef);          NSRange range = NSMakeRange(lineRange.location, lineRange.length);            NSString *lineString = [text substringWithRange:range];          [linesArray addObject:lineString];      }      return (NSArray *)linesArray;  }</code></pre>    <p>使用此方法,可以获取每行的文字的内容。</p>    <p>第二步:判断是否含有Emoji表情</p>    <pre>  <code class="language-objectivec">//判断是否含有Emoji表情  + (BOOL)stringContainsEmoji:(NSString *)string  {      __block BOOL returnValue =NO;      [string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {          const unichar hs = [substring characterAtIndex:0];          // surrogate pair          if (0xd800) {              if (0xd800 <= hs && hs <= 0xdbff) {                  if (substring.length > 1) {                      const unichar ls = [substring characterAtIndex:1];                      const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;                      if (0x1d000 <= uc && uc <= 0x1f77f) {                          returnValue =YES;                      }                  }              }else if (substring.length > 1) {                  const unichar ls = [substring characterAtIndex:1];                  if (ls == 0x20e3) {                      returnValue =YES;                  }              }else {                  // non surrogate                  if (0x2100 <= hs && hs <= 0x27ff) {                      returnValue =YES;                  }else if (0x2B05 <= hs && hs <= 0x2b07) {                      returnValue =YES;                  }else if (0x2934 <= hs && hs <= 0x2935) {                      returnValue =YES;                  }else if (0x3297 <= hs && hs <= 0x3299) {                      returnValue =YES;                  }else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) {                      returnValue =YES;                  }              }          }      }];      return returnValue;  }</code></pre>    <p>第三步:循环存储每行文字内容的数组,处理字符串</p>    <pre>  <code class="language-objectivec">- (NSMutableAttributedString *)changeLineSpacing:(NSArray *)stringList {      NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc] init];      for (NSString *string in stringList) {  //如果含有Emoji表情,不做处理          if ([NSString stringContainsEmoji:string]) {              NSMutableAttributedString *contentEmojistring = [[NSMutableAttributedString alloc] initWithString:string];              [mutableString appendAttributedString:contentEmojistring];          }else { //否则设置段落样式,行高为4(这个高度要根据自己的需求慢慢的试)              NSMutableAttributedString *unContentEmojistring = [[NSMutableAttributedString alloc] initWithString:string];              NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];              paragraphStyle.lineSpacing = 4;              [unContentEmojistring addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [unContentEmojistring length])];              [mutableString appendAttributedString:unContentEmojistring];          }      }      return mutableString; //返回最后处理完成的字符串  }</code></pre>    <p>此时你看到的效果是这样的</p>    <p><img src="https://simg.open-open.com/show/e6023107bbfc27c3ae44210ae8a68cb2.png"></p>    <p>这样就实现了需求了。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/96df4e0ffac7</p>    <p> </p>