15
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-04-09

Swift3での文字数取得

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

これらの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 で取得ができるようです。
※ ただし、全てのパターンが正常かどうかは不明です。

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

15
19
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?