需求背景:
在及時(shí)通訊聊天中,一般可以嵌套在文本中的表情為特殊字符,在UI顯示的時(shí)候,再通過(guò)富文本處理為相應(yīng)的圖片。
首先,微信里面可以識(shí)別的表情符為“[]”或者“/:*”。
第一步,我們得準(zhǔn)備好相應(yīng)的表情png。
然后,編寫相應(yīng)的字典,讓圖片和字符一一對(duì)應(yīng)。
如下:
<?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>[發(fā)呆]</key>
<string>Expression_4</string>
<key>[發(fā)怒]</key>
<string>Expression_12</string>
<key>[發(fā)抖]</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>[太陽(yáng)]</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>[強(qiáng)]</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>[愛(ài)你]</key>
<string>Expression_88</string>
<key>[愛(ài)心]</key>
<string>Expression_67</string>
<key>[愛(ài)情]</key>
<string>Expression_91</string>
<key>[豬頭]</key>
<string>Expression_63</string>
<key>[玫瑰]</key>
<string>Expression_64</string>
<key>[瓢蟲]</key>
<string>Expression_74</string>
<key>[疑問(wèn)]</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>[調(diào)皮]</key>
<string>Expression_13</string>
<key>[足球]</key>
<string>Expression_73</string>
<key>[跳繩]</key>
<string>Expression_99</string>
<key>[跳跳]</key>
<string>Expression_93</string>
<key>[轉(zhuǎn)圈]</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>[陰險(xiǎn)]</key>
<string>Expression_52</string>
<key>[難過(guò)]</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>
通過(guò)plist文件先將表情符和對(duì)應(yīng)圖片名一一對(duì)應(yīng),這個(gè)方法,也可以用來(lái)做自定義表情。
第二步,就是拿到含有表情字符串后,對(duì)相對(duì)應(yīng)的字符進(jìn)行檢測(cè)并一一替換,主要用到了以下幾個(gè)類
NSMutableAttributedString
NSTextAttachment
主題思路如下:
首先利用正則表達(dá)式匹配“[]”內(nèi)容,然后利用NSTextAttachment將表情替換上去。
按照這個(gè)思路去添加,先列出主要方法吧:
/// 替換字符中的表情為圖片的方法
///
/// - Parameters:
/// - string: 需要替換的字符
/// - expression: 替換的規(guī)則類
/// - 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 的值,從下一個(gè)表情的位置開始
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類,是自定義的一個(gè)負(fù)責(zé)處理,表情plist文件,匹配的正則表達(dá)式,以及圖片存放bundle的類。
SWTextAttachment主要是繼承了NSTextAttachment類,負(fù)責(zé)管理圖片,在里面自定義并ovverride了一些父類的方法,目的是為了適配字符本身的大小,以及避免圖片失真的方法。
以上思路主要參考了,MLLabel的實(shí)現(xiàn),傳送門如下:
https://github.com/molon/MLLabel
進(jìn)一步,是關(guān)于“/:XXX” 類型的表情字符,這類字符由于只有/:開頭的特征,長(zhǎng)度不定,所以無(wú)法用正則去匹配,我的解決策略如下:
首先,收集所有表示表情的字符:
<?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>[發(fā)呆]</string>
<key>/:8-)</key>
<string>[得意]</string>
<key>/::<</key>
<string>[流淚]</string>
<key>/::$</key>
<string>[害羞]</string>
<key>/::X</key>
<string>[閉嘴]</string>
<key>/::Z</key>
<string>[睡]</string>
<key>/::'(</key>
<string>[大哭]</string>
<key>/::-|</key>
<string>[尷尬]</string>
<key>/::@</key>
<string>[發(fā)怒]</string>
<key>/::P</key>
<string>[調(diào)皮]</string>
<key>/::D</key>
<string>[呲牙]</string>
<key>/::O</key>
<string>[驚訝]</string>
<key>/::(</key>
<string>[難過(guò)]</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>/::></key>
<string>[憨笑]</string>
<key>/::,@</key>
<string>[悠閑]</string>
<key>/:,@f</key>
<string>[奮斗]</string>
<key>/::-S</key>
<string>[咒罵]</string>
<key>/:?</key>
<string>[疑問(wèn)]</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>/:&-(</key>
<string>[糗大了]</string>
<key>/:B-(</key>
<string>[壞笑]</string>
<key>/:<@</key>
<string>[左哼哼]</string>
<key>/:@></key>
<string>[右哼哼]</string>
<key>/::-O</key>
<string>[哈欠]</string>
<key>/:>-|</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>/:<W></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>[愛(ài)心]</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>[太陽(yáng)]</string>
<key>/:gift</key>
<string>[禮物]</string>
<key>/:hug</key>
<string>[擁抱]</string>
<key>/:strong</key>
<string>[強(qiáng)]</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>[愛(ài)你]</string>
<key>/:no</key>
<string>[NO]</string>
<key>/:ok</key>
<string>[OK]</string>
<key>/:<L></key>
<string>[飛吻]</string>
<key>/:love</key>
<string>[愛(ài)情]</string>
<key>/:jump</key>
<string>[跳跳]</string>
<key>/:shake</key>
<string>[發(fā)抖]</string>
<key>/:<O></key>
<string>[慪火]</string>
<key>/:circle</key>
<string>[轉(zhuǎn)圈]</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>[激動(dòng)]</string>
<key>/:hiphot</key>
<string>[亂舞]</string>
<key>/:kiss</key>
<string>[獻(xiàn)舞]</string>
<key>/:<&</key>
<string>[左太極]</string>
<key>/:&></key>
<string>[右太極]</string>
</dict>
</plist>
然后,遍歷這個(gè)字典:
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
}
}
}
}
如果有,就替換,沒(méi)有及繼續(xù)遍歷,直到字符串中沒(méi)有"/:"為止。
這個(gè)算法的時(shí)間復(fù)雜度為O(n), n為字典的長(zhǎng)度。
但個(gè)人認(rèn)為實(shí)際應(yīng)該可以優(yōu)化。
經(jīng)過(guò)替換以后,用[]的解決方案即可解決。