美文网首页IOS开发iOS语法技巧UI
iOS 聊天信息图文混排

iOS 聊天信息图文混排

作者: 番茄冰 | 来源:发表于2016-06-06 16:19 被阅读2258次

         最近在做一个表情云的项目,提供表情键盘给其他开发者。在写演示Demo的时候,有一个功能点是要支持聊天信息的图文混排。当时听到这个需求的时候,大脑里第一反应是用webView来通过htmlstring来实现这个功能,不过webView是一个能不用最好不用的东西,因为它的内存管理特别可怕。所以我研究了其他方法,确实存在一个更好的方法,那就是使用NSTextAttachment和NSAttributedString。

         其实原理很简单,那就是把要混排的图片当成一个字符来显示,这就要用到NSAttributedString了。当我们需要在文字中高亮某个词语,或是改变行距等等,各种普通的NSString+UILabel做不到的事情都可以用NSAttributedString来做。

         它有一个工厂方法,这是个iOS7.0新增的接口。

    + (NSAttributedString *)attributedStringWithAttachment:(NSTextAttachment *)attachment NS_AVAILABLE(10_0, 7_0);
    

         通过这个方法可以把NSTextAttachment转化成NSAttributedString 。只要把图片转换成一个NSTextAttachment就可以和其他NSAttributedString添加在一起了。

         那么,要如何把图片转换为NSTextAttachment呢?同样很简单。
    NSTextAttachment有一个属性image,只需要把图片赋值给这个属性就可以了。

    NSTextAttachment *textAttachment = [[NSTextAttachment alloc]init];
    textAttachment.image = image;
    

         这样我们就得到了一个NSTextAttachment。然后再把他转换为NSAttributedString 。

    NSAttributedString *imageStr = [NSAttributedString attributedStringWithAttachment:textAttachment];
    

         有了NSAttributedString,相信大家下一步都知道该怎么做了,我就不多说了。

         不过有一个问题,我目前还没有找到特别漂亮的方式来解决,那就是这样插入的图片大小和文字不太一样,是根据图片原先大小来的,目前我的解决方法是,设置NSTextAttachment的属性bounds,强行赋值来进行排版。

         来一张效果图

    Paste_Image.png

         看起来还不错,至此图文混排的基本功能就搞定了。

         但是到目前为止实现的功能仅仅是实现将指定的UIImage和文字混排在一起而已。然而我的最终目标是实现聊天信息中的图文混排。那么该怎么做呢?

         一开始我也是一头雾水,然后我想到了微信平时发默认表情时候的场景,一下子豁然开朗。究竟是怎么回事呢?让我们来看一下微信里的默认表情的使用的情况。

    EEAB01FD10C218C3C561A95051710844.png

         不难发现,当我们点击一个默认表情的时候,在微信的文字发送框里,就会出现一个[xxx]格式的字符串,此时点击发送的话,就可以看到发出去的聊天信息里带有了默认表情。

    E5D772C423407013457AB1E757C2F403.png

         那么这个要如何做到呢?让我们来分析一下这个现象,很显然,微信把[调皮]和[撇嘴]转换为了图片显示,显示的原理很简单,就如上文我们说的,只需要初始化两个UIImage,然后赋值给NSTextAttachment并转换为NSSAttributedString就可以了。问题的核心在于如何知道哪些文字需要我们来转换。

         显然微信的做法是将所有方括号"[]"里面的文字替换为NSTextAttachment转换成的NSSAttributedString了。也就是说,我们需要找出所有在[]里的词组。如果想要做到这一点,
    就不得不提到一个概念,正则表达式。

         不过有关正则表达式的知识实在是太多,而且我也只是略知一二,所以就不在这里献丑了。我就直接上代码了。简书里面markdown的转义我不是很会用,所以我就贴截图吧。

    4899FCB7-845A-4690-B440-213509849C48.png

         这段代码的意思就是用regex_emoji这段正则表达式去匹配字符,获得的结果存在resultArray数组里。

         其他东西我想也没什么好解释的,我就简单的解释一下这个正则表达式的含义。
    最左边的\\\\\\\\[是用来匹配“[”的,最右边的\\\\\\\\]是用来匹配“]”的,而^\\\\\\\\[\\\\\\\\]代表了除[]以外的字符,在外面加一个[]*就表示匹配任意数量的除[]以外的字符,这样就可以匹配到[xxx]这个格式的字符了。

         让我们来打一下log检测一下,当text赋值为@"f[等等[b哦]]哦a[a哈f]]]"的时候,resultArray的结果为

    E1054269-8064-48FC-A884-E8AF246B17B3.png

         可以看到,log里的两个range确实正确的对应了我们想要的结果。接下来我们就要对这个数组进行解析。

    for (NSInteger i = 0; i  < [resultArray count]; ++i) {
        NSTextCheckingResult *result = resultArray[i];
        NSRange range =  [result range];
        NSString *subStr = [text substringWithRange:range];
        NSLog(@"%@",subStr);
    }
    

         看一下输出的内容,可以看出我们已经得到了要转换的文字了。


    6ADBA36C-C61F-4112-9C4E-09B26E165128.png

         接下来要做的事就简单了,根据这个格式的字符去找对应名字的图片,然后再以NSTextAttachment的方式转换为NSAttributedString,然后根据相对应的NSRange去替换原先的文本,就可以了。

    NSMutableDictionary *imageDic = [NSMutableDictionary dictionaryWithCapacity:2];
    [imageDic setObject:imageStr forKey:@"image"];
    [imageDic setObject:[NSValue valueWithRange:range] forKey:@"range"];
    [imageArray addObject:imageDic];
    

         解释一下,imageStr就是已经转换为NSAttributedString的NSTextAttachment,这里它和range组成一个字典保存到一个imageArray里面去。

    NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc]initWithString:text];
    for (NSInteger i = [imageArray count]-1; i>=0; i--) {
        NSDictionary *dic = imageArray[i];
        NSRange range = [dic[@"range"] rangeValue];
        [attributeString replaceCharactersInRange:range withAttributedString:dic[@"image"]];
    }
    

         遍历一下imageArray的数组,依次取出对应的范围和转换后的NSAttributedString,然后来替换对应的地方。注意这里使用的是倒序遍历。因为如果使用正序遍历的话,当替换掉一组词后,长度发生了变化,原先保存的NSRange可能就无法对应到正确的位置了。

         至此,图文混排的功能就真正实现了。最终的效果就如下图所示。

    4E7B1485A528B3328E6266F824134FF1.png 9EEFCD208B99D15DEBF7863F6BDCEECB.png

    相关文章

      网友评论

      • IOS_绿豆糕:replaceCharactersInRange的话 高度会高出许多,怎么解决的?
        李小南:你好, 我也遇到了, 请问你是怎么解决的呢
      • 091e03f6b9a1:楼主您好,请问你的label自适应怎么计算的
      • JohnQ:博主 我想问一下你的那个删除方法是如何实现的?我想知道你在删除的时候如何做到动态的删除几个字段
      • Hom_zhang:图文混排就是解决你用字符替换的,你这样搞倒退了
      • JohnQ:博主,我在用你的方法进行写了后 用这样的一个字符串进行测试的时候就无法得到内容 [白眼45677544],正确的情况下应该是发出去的还是这样,可是我发出去后内容就没有了 就是空白
        番茄冰:@JohnQ 你在项目里拖一个图片起名叫白眼45677544应该就行了
      • JohnQ:博主,你的这image是如何获取到的
      • JohnQ:博主你写的这个还有对应的demo啊
      • 赛赛_lzx:你说的微信那个表情,不是emoji表情,是微信的自定义表情。emoji表情是比如搜狗输入法自带的,你输出,textfield上也直接显示出来,没有[笑脸]这样。emoji实际上就是文字相当于,只不过占4个字节,而一般utf-8的汉字占3个字节,使用emoji表情的话,不需要考虑图文混排,直接用就可以了。
        赛赛_lzx:emoji表情,如果后台用的mysql,存储的时候必须是要支持才行,度娘有解决办法,修改数据库字段类型或者还是标得类型,我不是做后台的,我们后台的修改了一下就好了。
        Jayne_Kuo:@赛赛_lzx 但是接收的表情居然是????这个怎么解决?
        番茄冰:@赛赛_lzx 谢谢指出,已改正
      • chad_it:您好,希望看到能回复我,有些问题请教 谢谢
        番茄冰:@不要把世界让给你鄙视的人 什么问题?
      • 小小Q吖:就是 不知道 聊天 功能怎么实现的
        松少:@就是拽嘿嘿 xmpp
      • sunhq:挺好的,回头试试

      本文标题:iOS 聊天信息图文混排

      本文链接:https://www.haomeiwen.com/subject/jeaqdttx.html