美文网首页
用Swift处理微信、qq的表情及Emoji

用Swift处理微信、qq的表情及Emoji

作者: 莫寂岚 | 来源:发表于2018-08-15 12:28 被阅读160次

需求背景:
在及时通讯聊天中,一般可以嵌套在文本中的表情为特殊字符,在UI显示的时候,再通过富文本处理为相应的图片。


首先,微信里面可以识别的表情符为“[]”或者“/:*”。
第一步,我们得准备好相应的表情png。
然后,编写相应的字典,让图片和字符一一对应。
如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>[NO]</key>
    <string>Expression_89</string>
    <key>[OK]</key>
    <string>Expression_90</string>
    <key>[乒乓]</key>
    <string>Expression_60</string>
    <key>[亲亲]</key>
    <string>Expression_53</string>
    <key>[便便]</key>
    <string>Expression_75</string>
    <key>[偷笑]</key>
    <string>Expression_21</string>
    <key>[傲慢]</key>
    <string>Expression_24</string>
    <key>[再见]</key>
    <string>Expression_40</string>
    <key>[冷汗]</key>
    <string>Expression_18</string>
    <key>[凋谢]</key>
    <string>Expression_65</string>
    <key>[刀]</key>
    <string>Expression_72</string>
    <key>[勾引]</key>
    <string>Expression_85</string>
    <key>[发呆]</key>
    <string>Expression_4</string>
    <key>[发怒]</key>
    <string>Expression_12</string>
    <key>[发抖]</key>
    <string>Expression_94</string>
    <key>[可怜]</key>
    <string>Expression_55</string>
    <key>[右哼哼]</key>
    <string>Expression_47</string>
    <key>[吐]</key>
    <string>Expression_20</string>
    <key>[吓]</key>
    <string>Expression_54</string>
    <key>[呲牙]</key>
    <string>Expression_14</string>
    <key>[咒骂]</key>
    <string>Expression_32</string>
    <key>[咖啡]</key>
    <string>Expression_61</string>
    <key>[哈欠]</key>
    <string>Expression_48</string>
    <key>[啤酒]</key>
    <string>Expression_58</string>
    <key>[嘘]</key>
    <string>Expression_34</string>
    <key>[嘴唇]</key>
    <string>Expression_66</string>
    <key>[回头]</key>
    <string>Expression_98</string>
    <key>[困]</key>
    <string>Expression_26</string>
    <key>[坏笑]</key>
    <string>Expression_45</string>
    <key>[大哭]</key>
    <string>Expression_10</string>
    <key>[太阳]</key>
    <string>Expression_77</string>
    <key>[奋斗]</key>
    <string>Expression_31</string>
    <key>[委屈]</key>
    <string>Expression_50</string>
    <key>[害羞]</key>
    <string>Expression_7</string>
    <key>[尴尬]</key>
    <string>Expression_11</string>
    <key>[左哼哼]</key>
    <string>Expression_46</string>
    <key>[差劲]</key>
    <string>Expression_87</string>
    <key>[弱]</key>
    <string>Expression_81</string>
    <key>[强]</key>
    <string>Expression_80</string>
    <key>[得意]</key>
    <string>Expression_5</string>
    <key>[微笑]</key>
    <string>Expression_1</string>
    <key>[心碎]</key>
    <string>Expression_68</string>
    <key>[快哭了]</key>
    <string>Expression_51</string>
    <key>[怄火]</key>
    <string>Expression_95</string>
    <key>[悠闲]</key>
    <string>Expression_30</string>
    <key>[惊恐]</key>
    <string>Expression_27</string>
    <key>[惊讶]</key>
    <string>Expression_15</string>
    <key>[愉快]</key>
    <string>Expression_22</string>
    <key>[憨笑]</key>
    <string>Expression_29</string>
    <key>[抓狂]</key>
    <string>Expression_19</string>
    <key>[投降]</key>
    <string>Expression_100</string>
    <key>[抠鼻]</key>
    <string>Expression_42</string>
    <key>[抱拳]</key>
    <string>Expression_84</string>
    <key>[拥抱]</key>
    <string>Expression_79</string>
    <key>[拳头]</key>
    <string>Expression_86</string>
    <key>[握手]</key>
    <string>Expression_82</string>
    <key>[撇嘴]</key>
    <string>Expression_2</string>
    <key>[擦汗]</key>
    <string>Expression_41</string>
    <key>[敲打]</key>
    <string>Expression_39</string>
    <key>[晕]</key>
    <string>Expression_35</string>
    <key>[月亮]</key>
    <string>Expression_76</string>
    <key>[流汗]</key>
    <string>Expression_28</string>
    <key>[流泪]</key>
    <string>Expression_6</string>
    <key>[炸弹]</key>
    <string>Expression_71</string>
    <key>[爱你]</key>
    <string>Expression_88</string>
    <key>[爱心]</key>
    <string>Expression_67</string>
    <key>[爱情]</key>
    <string>Expression_91</string>
    <key>[猪头]</key>
    <string>Expression_63</string>
    <key>[玫瑰]</key>
    <string>Expression_64</string>
    <key>[瓢虫]</key>
    <string>Expression_74</string>
    <key>[疑问]</key>
    <string>Expression_33</string>
    <key>[疯了]</key>
    <string>Expression_36</string>
    <key>[白眼]</key>
    <string>Expression_23</string>
    <key>[睡]</key>
    <string>Expression_9</string>
    <key>[磕头]</key>
    <string>Expression_97</string>
    <key>[礼物]</key>
    <string>Expression_78</string>
    <key>[篮球]</key>
    <string>Expression_59</string>
    <key>[糗大了]</key>
    <string>Expression_44</string>
    <key>[胜利]</key>
    <string>Expression_83</string>
    <key>[色]</key>
    <string>Expression_3</string>
    <key>[菜刀]</key>
    <string>Expression_56</string>
    <key>[蛋糕]</key>
    <string>Expression_69</string>
    <key>[衰]</key>
    <string>Expression_37</string>
    <key>[西瓜]</key>
    <string>Expression_57</string>
    <key>[调皮]</key>
    <string>Expression_13</string>
    <key>[足球]</key>
    <string>Expression_73</string>
    <key>[跳绳]</key>
    <string>Expression_99</string>
    <key>[跳跳]</key>
    <string>Expression_93</string>
    <key>[转圈]</key>
    <string>Expression_96</string>
    <key>[鄙视]</key>
    <string>Expression_49</string>
    <key>[酷]</key>
    <string>Expression_17</string>
    <key>[闪电]</key>
    <string>Expression_70</string>
    <key>[闭嘴]</key>
    <string>Expression_8</string>
    <key>[阴险]</key>
    <string>Expression_52</string>
    <key>[难过]</key>
    <string>Expression_16</string>
    <key>[飞吻]</key>
    <string>Expression_92</string>
    <key>[饥饿]</key>
    <string>Expression_25</string>
    <key>[饭]</key>
    <string>Expression_62</string>
    <key>[骷髅]</key>
    <string>Expression_38</string>
    <key>[鼓掌]</key>
    <string>Expression_43</string>
</dict>
</plist>

通过plist文件先将表情符和对应图片名一一对应,这个方法,也可以用来做自定义表情。


第二步,就是拿到含有表情字符串后,对相对应的字符进行检测并一一替换,主要用到了以下几个类

NSMutableAttributedString
NSTextAttachment

主题思路如下:
首先利用正则表达式匹配“[]”内容,然后利用NSTextAttachment将表情替换上去。

按照这个思路去添加,先列出主要方法吧:

 /// 替换字符中的表情为图片的方法
    ///
    /// - Parameters:
    ///   - string: 需要替换的字符
    ///   - expression: 替换的规则类
    /// - Returns: 返回替换后的富文本
    public class func expressionAttributedString(string: NSAttributedString, expression: SWExpression) -> NSAttributedString {
        let target: NSMutableAttributedString = string.mutableCopy() as! NSMutableAttributedString
        if target.length <= 0 {
            return target
        }
        let tempAttribute = NSMutableAttributedString.init()
        // 处理表情
        let resArr = expression.expressionRegularExpression.matches(in: target.string, options: .withTransparentBounds, range: NSRange.init(location: 0, length: target.length))
        var location: Int = 0;
        for resultText: NSTextCheckingResult in resArr {
            let range: NSRange = resultText.range
            let subString: NSAttributedString = target.attributedSubstring(from: NSRange.init(location: location, length: range.location - location))
            // 先把非表情部分加上去
            tempAttribute.append(subString)
            // 修改location 的值,从下一个表情的位置开始
            location = NSMaxRange(range)
            
            let expressionStr = target.attributedSubstring(from: range)
            let imageName: String = expression.expressionMap.object(forKey: expressionStr.string) as? String ?? ""
            // 如果图片名存在
            if imageName.count > 0 {
                let bundle = Bundle.init(url: Bundle.main.url(forResource: expression.bundleName, withExtension: ".bundle")!)
                let image: UIImage = UIImage.init(named: imageName, in: bundle!, compatibleWith: nil)!
                let textAttachment = SWTextAttachment.init(lineHeightMultiple: 1.00, imageAspectRatio: image.size.width/image.size.height) { (imageBounds, textContainer, charIndex, textAttachment) -> UIImage in
                    return image
                }
                let attachmentString: NSMutableAttributedString = NSAttributedString.init(attachment: textAttachment).mutableCopy() as! NSMutableAttributedString
                expressionStr.enumerateAttributes(in: NSRange.init(location: 0, length: expressionStr.length), options: .longestEffectiveRangeNotRequired) { (attrs, range, stop) in
                    if attrs.count>0 && range.length==expressionStr.length {
                        attachmentString.addAttributes(attrs, range: NSRange.init(location: 0, length: attachmentString.length))
                    }
                }
                tempAttribute.append(attachmentString)
            } else {
                tempAttribute.append(expressionStr)
            }
        }
        if location < target.length {
            let range = NSRange.init(location: location, length: target.length - location)
            let sub = target.attributedSubstring(from: range)
            tempAttribute.append(sub)
        }
        return tempAttribute
    }

其中expression类,是自定义的一个负责处理,表情plist文件,匹配的正则表达式,以及图片存放bundle的类。
SWTextAttachment主要是继承了NSTextAttachment类,负责管理图片,在里面自定义并ovverride了一些父类的方法,目的是为了适配字符本身的大小,以及避免图片失真的方法。
以上思路主要参考了,MLLabel的实现,传送门如下:
https://github.com/molon/MLLabel


进一步,是关于“/:XXX” 类型的表情字符,这类字符由于只有/:开头的特征,长度不定,所以无法用正则去匹配,我的解决策略如下:
首先,收集所有表示表情的字符:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>/::)</key>
    <string>[微笑]</string>
    <key>/::~</key>
    <string>[撇嘴]</string>
    <key>/::B</key>
    <string>[色]</string>
    <key>/::|</key>
    <string>[发呆]</string>
    <key>/:8-)</key>
    <string>[得意]</string>
    <key>/::&lt;</key>
    <string>[流泪]</string>
    <key>/::$</key>
    <string>[害羞]</string>
    <key>/::X</key>
    <string>[闭嘴]</string>
    <key>/::Z</key>
    <string>[睡]</string>
    <key>/::&apos;(</key>
    <string>[大哭]</string>
    <key>/::-|</key>
    <string>[尴尬]</string>
    <key>/::@</key>
    <string>[发怒]</string>
    <key>/::P</key>
    <string>[调皮]</string>
    <key>/::D</key>
    <string>[呲牙]</string>
    <key>/::O</key>
    <string>[惊讶]</string>
    <key>/::(</key>
    <string>[难过]</string>
    <key>/::+</key>
    <string>[酷]</string>
    <key>/::Q</key>
    <string>[抓狂]</string>
    <key>/::T</key>
    <string>[吐]</string>
    <key>/:,@P</key>
    <string>[偷笑]</string>
    <key>/:,@-D</key>
    <string>[愉快]</string>
    <key>/::d</key>
    <string>[白眼]</string>
    <key>/:,@o</key>
    <string>[傲慢]</string>
    <key>/::g</key>
    <string>[饥饿]</string>
    <key>/:|-)</key>
    <string>[困]</string>
    <key>/::!</key>
    <string>[惊恐]</string>
    <key>/::L</key>
    <string>[流汗]</string>
    <key>/::&gt;</key>
    <string>[憨笑]</string>
    <key>/::,@</key>
    <string>[悠闲]</string>
    <key>/:,@f</key>
    <string>[奋斗]</string>
    <key>/::-S</key>
    <string>[咒骂]</string>
    <key>/:?</key>
    <string>[疑问]</string>
    <key>/:,@x</key>
    <string>[嘘]</string>
    <key>/:,@@</key>
    <string>[晕]</string>
    <key>/::8</key>
    <string>[疯了]</string>
    <key>/:,@!</key>
    <string>[衰]</string>
    <key>/:!!!</key>
    <string>[骷髅]</string>
    <key>/:xx</key>
    <string>[敲打]</string>
    <key>/:bye</key>
    <string>[再见]</string>
    <key>/:wipe</key>
    <string>[擦汗]</string>
    <key>/:dig</key>
    <string>[抠鼻]</string>
    <key>/:handclap</key>
    <string>[鼓掌]</string>
    <key>/:&amp;-(</key>
    <string>[糗大了]</string>
    <key>/:B-(</key>
    <string>[坏笑]</string>
    <key>/:&lt;@</key>
    <string>[左哼哼]</string>
    <key>/:@&gt;</key>
    <string>[右哼哼]</string>
    <key>/::-O</key>
    <string>[哈欠]</string>
    <key>/:&gt;-|</key>
    <string>[鄙视]</string>
    <key>/:P-(</key>
    <string>[委屈]</string>
    <key>/::*</key>
    <string>[亲亲]</string>
    <key>/:@x</key>
    <string>[吓]</string>
    <key>/:8*</key>
    <string>[可怜]</string>
    <key>/:pd</key>
    <string>[菜刀]</string>
    <key>/:&lt;W&gt;</key>
    <string>[西瓜]</string>
    <key>/:beer</key>
    <string>[啤酒]</string>
    <key>/:basketb</key>
    <string>[篮球]</string>
    <key>/:oo</key>
    <string>[乒乓]</string>
    <key>/:coffee</key>
    <string>[咖啡]</string>
    <key>/:eat</key>
    <string>[饭]</string>
    <key>/:pig</key>
    <string>[猪头]</string>
    <key>/:rose</key>
    <string>[玫瑰]</string>
    <key>/:fade</key>
    <string>[凋谢]</string>
    <key>/:showlove</key>
    <string>[嘴唇]</string>
    <key>/:heart</key>
    <string>[爱心]</string>
    <key>/:break</key>
    <string>[心碎]</string>
    <key>/:cake</key>
    <string>[蛋糕]</string>
    <key>/:li</key>
    <string>[闪电]</string>
    <key>/:bome</key>
    <string>[炸弹]</string>
    <key>/:kn</key>
    <string>[刀子]</string>
    <key>/:footb</key>
    <string>[足球]</string>
    <key>/:ladybug</key>
    <string>[瓢虫]</string>
    <key>/:shit</key>
    <string>[便便]</string>
    <key>/:moon</key>
    <string>[月亮]</string>
    <key>/:sun</key>
    <string>[太阳]</string>
    <key>/:gift</key>
    <string>[礼物]</string>
    <key>/:hug</key>
    <string>[拥抱]</string>
    <key>/:strong</key>
    <string>[强]</string>
    <key>/:weak</key>
    <string>[弱]</string>
    <key>/:share</key>
    <string>[握手]</string>
    <key>/:v</key>
    <string>[胜利]</string>
    <key>/:@)</key>
    <string>[抱拳]</string>
    <key>/:jj</key>
    <string>[勾引]</string>
    <key>/:@@</key>
    <string>[拳头]</string>
    <key>/:bad</key>
    <string>[差劲]</string>
    <key>/:lvu</key>
    <string>[爱你]</string>
    <key>/:no</key>
    <string>[NO]</string>
    <key>/:ok</key>
    <string>[OK]</string>
    <key>/:&lt;L&gt;</key>
    <string>[飞吻]</string>
    <key>/:love</key>
    <string>[爱情]</string>
    <key>/:jump</key>
    <string>[跳跳]</string>
    <key>/:shake</key>
    <string>[发抖]</string>
    <key>/:&lt;O&gt;</key>
    <string>[怄火]</string>
    <key>/:circle</key>
    <string>[转圈]</string>
    <key>/:kotow</key>
    <string>[磕头]</string>
    <key>/:turn</key>
    <string>[回头]</string>
    <key>/:skip</key>
    <string>[跳绳]</string>
    <key>/:oY</key>
    <string>[投降]</string>
    <key>/:#-0</key>
    <string>[激动]</string>
    <key>/:hiphot</key>
    <string>[乱舞]</string>
    <key>/:kiss</key>
    <string>[献舞]</string>
    <key>/:&lt;&amp;</key>
    <string>[左太极]</string>
    <key>/:&amp;&gt;</key>
    <string>[右太极]</string>
</dict>
</plist>

然后,遍历这个字典:

extension String {
    mutating func emojiParse(emojiDic: Dictionary<String, String>) -> Void {
        for (key, value) in emojiDic {
            if self.contains(key) {
               self = self.replacingOccurrences(of: key, with: value)
            }
            if !self.contains("/:") {
                break
            }
        }
    }
}

如果有,就替换,没有及继续遍历,直到字符串中没有"/:"为止。
这个算法的时间复杂度为O(n), n为字典的长度。
但个人认为实际应该可以优化。
经过替换以后,用[]的解决方案即可解决。

相关文章

网友评论

      本文标题:用Swift处理微信、qq的表情及Emoji

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