含有Emoji表情的文本计算高度
brighttang
8年前
<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>