はじめに
Objective-C時代から絵文字の判定方法には 絵文字の文字コードを全部メモリーに持っておいて照合する という泥臭い方法を採っていたのですが、iOSのバージョンアップに伴う絵文字の追加に追従するのが面倒くさいので、もう少しスマートに判定できないか考えてみました。
- 2016/12/16 Xcode8.2 + iOS 10.2 頂いたコメント等を元に再編
考えたもの
仕組みは簡単、CoreTextを使ってAppleColorEmojiフォントに対し、文字コードを渡し、グリフ値(フォントの文字のインデックス値)へ変換できたら絵文字とみなす、というものです。
ただ、AppleColorEmojiフォントは何故か0,1,2,3...と言った文字のグリフも持っているため、その辺りだけ例外的に除外してあげる必要があります。
また、String.CharacterViewでは一部の絵文字が正しく処理されない事が判っています。
let w2 = "👩👩👧👦"
w2.characters.count // 4
そのため、String.CharacterViewではなくString.enumerateSubstringsを使って書記素クラスタへ分解、分解された文字列ごとに絵文字かどうかを判定すると今の所うまく行くようです。
以下、完成したソースです。
import UIKit
fileprivate var appleColorEmojiFont = CTFontCreateWithName("AppleColorEmoji" as CFString, 20, nil)
extension String {
var isAppleColorEmoji: Bool {
let chars = Array(self.utf16)
if chars.count == 1 && chars[0] <= 57 { // 制御文字やスペース、数字を除外
return false
}
var glyphs = [CGGlyph](repeating: 0, count: chars.count)
return CTFontGetGlyphsForCharacters(appleColorEmojiFont, chars, &glyphs, glyphs.count)
}
}
実験結果
let str = "あ🍣😱🇨🇳0123*#👪👩👩👧👦👯ABC漢\n _❄️❄︎"
str.enumerateSubstrings(in: str.startIndex..<str.endIndex, options: .byComposedCharacterSequences) { str2, _, _, _ in
if let str2 = str2 {
let result = str2.isAppleColorEmoji ? "[絵文字]" : ""
let code = str2.utf16.map({ String(format: "0x%04x", $0) }).joined(separator: " ")
print("|\"\(str2)\"|\(result)|\(code)|")
}
}
| 文字 | 絵文字判定 | 文字コード |
|---|---|---|
| "あ" | 0x3042 | |
| "🍣" | [絵文字] | 0xd83c 0xdf63 |
| "😱" | [絵文字] | 0xd83d 0xde31 |
| "🇨🇳" | [絵文字] | 0xd83c 0xdde8 0xd83c 0xddf3 |
| "0" | 0x0030 | |
| "1" | 0x0031 | |
| "2" | 0x0032 | |
| "3" | 0x0033 | |
| "*" | 0x002a | |
| "#" | 0x0023 | |
| "👪" | [絵文字] | 0xd83d 0xdc6a |
| "👩👩👧👦" | [絵文字] | 0xd83d 0xdc69 0x200d 0xd83d 0xdc69 0x200d 0xd83d 0xdc67 0x200d 0xd83d 0xdc66 |
| "👯" | [絵文字] | 0xd83d 0xdc6f |
| "A" | 0x0041 | |
| "B" | 0x0042 | |
| "C" | 0x0043 | |
| "漢" | 0x6f22 | |
| (改行) | 0x000a | |
| " " | 0x0020 | |
| "_" | 0x005f | |
| "❄️" | [絵文字] | 0x2744 0xfe0f |
| "❄︎" | 0x2744 0xfe0e |
考察
とりあえずうまく判定できているように見えます。
"❄️"と"❄︎"のように同じ文字コード+異体字セレクタで絵文字か否かを指定しているものも、正しく判定してくれているようです。
もし、誤判定しそうなものや、そもそも他にもっと良い方法がありましたら、情報提供していただけると助かります。
