Swift3で絵文字を含んだ文字列の長さを取得する方法

## Swift3での文字数取得

文字の長さを取得するためには、以下の4つのViewから適切なエンコードのものを選択します。

* [var characters: String.CharacterView](https://developer.apple.com/reference/swift/string.characterview)
* [var unicodeScalars: String.UnicodeScalarView](https://developer.apple.com/reference/swift/string.unicodescalarview)
* [var utf8: String.UTF8View](https://developer.apple.com/reference/swift/string.utf8view)
* [var utf16: String.UTF16View](https://developer.apple.com/reference/swift/string.utf16view)

これらのViewのcountプロパティが文字数になります。

## 絵文字の文字数

絵文字以外の文字であれば、characters.count を文字数として考えてよさそうですが、絵文字が複雑化してきたため、文字数として判断できないパターンがあります。

各Viewのcountの値

| 文字 | Character | UnicodeScalar | UTF8 | UTF16 |
|---|---:|---:|---:|---:|
| A | 1 | 1 | 1 | 1 |
| あ | 1 | 1 | 3 | 1 |
| 😀 | 1 | 1 | 4 | 2 |
| 🇵🇷 | 1 | 2 | 8 | 4 |
| 👍 | 1 | 1 | 4 | 2 |
| 👍🏽 | 2 | 2 | 8 | 4 |
| 👩‍👦 | 2 | 3 | 11 | 5 |
| 👩‍👩‍👦 | 3 | 5 | 18 | 8 |
| 👩‍👩‍👧‍👦 | 4 | 7 | 25 | 11 |

肌の色を変えるもの、複数人数の絵文字などは、characters.count != 文字数 とすることができません。
参考までに、Twitterアプリで投稿時の文字数を見てみた所、👩‍👩‍👧‍👦 は7文字分消費しているようなので、unicodeScalars.countで判断しているように見えます。

絵文字を含むの文字数を正確に取得する方法ですが、[CTLine​Get​Glyph​Count](https://developer.apple.com/reference/coretext/1509985-ctlinegetglyphcount) で取得ができるようです。
※ ただし、全てのパターンが正常かどうかは不明です。

```swift
extension String {
    var glyphCount: Int {
        let richText = NSAttributedString(string: self)
        let line = CTLineCreateWithAttributedString(richText)
        return CTLineGetGlyphCount(line)
    }
}
```

## テキストの文字数制限

UITextField.shouldChangeCharactersIn で 以下のようなサンプルをよく見かけます。

```swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let textCount = textField.text?.characters.count ?? 0
    let newLength = textCount + string.characters.count - range.length
    return newLength <= maxCount
}
```

ですが、この方法では、絵文字の場合はNGです。
ちなみにrange.lengthは、UTF16Viewの数になっているようなので、上例のようなCharacterViewの数とも合いません。

制限数がUTF16でないのであれば、ちゃんと反映後の文字列を作成して、その文字数をカウントするのが無難そうです。

```swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let currentText = textField.text ?? ""
    let prospectiveText = (currentText as NSString).replacingCharacters(in: range, with: string)
    let newLength = prospectiveText.characters.count
    return newLength <= maxCount
}
```

## 絵文字の判定

Unicodeでの判定

```swift
extension UnicodeScalar {
    var isEmoji: Bool {
        switch value {
        case 0x3030, 0x00AE, 0x00A9, // Special Characters
        0x1D000 ... 0x1F77F, // Emoticons
        0x2100 ... 0x27BF, // Misc symbols and Dingbats
        0xFE00 ... 0xFE0F, // Variation Selectors
        0x1F900 ... 0x1F9FF: // Supplemental Symbols and Pictographs
            return true

        default: return false
        }
    }
}
```

```swift
extension String {
    //絵文字が含まれているかどうか
    var containsEmoji: Bool {
        return !unicodeScalars.filter { $0.isEmoji }.isEmpty
    }

    // 絵文字を除去
    func stringByRemovingEmoji() -> String {
        return String(self.characters.filter { !$0.isEmoji })
    }
}

extension Character {
    fileprivate var isEmoji: Bool {
        let s = String(self).unicodeScalars
        if let unicode = UnicodeScalar(s[s.startIndex].value) {
            return unicode.isEmoji
        } else {
            return false
        }
    }
}
```

参考
http://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji/36258684