Swift3での文字数取得
文字の長さを取得するためには、以下の4つのViewから適切なエンコードのものを選択します。
- var characters: String.CharacterView
- var unicodeScalars: String.UnicodeScalarView
- var utf8: String.UTF8View
- var utf16: 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で判断しているように見えます。
絵文字を含むの文字数を正確に取得する方法ですが、CTLineGetGlyphCount で取得ができるようです。
※ ただし、全てのパターンが正常かどうかは不明です。
extension String {
    var glyphCount: Int {
        let richText = NSAttributedString(string: self)
        let line = CTLineCreateWithAttributedString(richText)
        return CTLineGetGlyphCount(line)
    }
}
テキストの文字数制限
UITextField.shouldChangeCharactersIn で 以下のようなサンプルをよく見かけます。
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でないのであれば、ちゃんと反映後の文字列を作成して、その文字数をカウントするのが無難そうです。
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での判定
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
        }
    }
}
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