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