絵文字を含む文字列を見た目の文字数で評価して家族を守る記事
iOSだと絵文字を含む文字列の文字数を数える有効な手段が(調べた限り)ありません。
twitterでもバグが出ます。
あと1文字打てるので絵文字を一個加えたら文字数オーバーになってしまいました。
このような問題をうまく解決するにはどうすればいいのか、最近考えていて、一番マシな手法と思われるものができたのでまとめました。
改良点ありましたらコメントしていただければと思います。
extension String {
var length: Int { return self.characters.count }
var emojiVisibleLength: Int {
var count = 0
enumerateSubstringsInRange(startIndex ..< endIndex, options: .ByComposedCharacterSequences) { _ in
count += 1
}
return count
}
private func calculateLengthWithEmoji(max: Int) -> Int {
var possibleLengths: [Int] = []
for subStringLength in min(length, emojiVisibleLength, max)...max*4 {
let lengthWithEmoji = self[startIndex..<startIndex.advancedBy(subStringLength)].emojiVisibleLength
switch lengthWithEmoji {
case let lengthWithEmoji where lengthWithEmoji < max:
continue
case let lengthWithEmoji where lengthWithEmoji == max:
possibleLengths.append(subStringLength)
case let lengthWithEmoji where lengthWithEmoji > max:
if possibleLengths.isEmpty {
possibleLengths.insert(subStringLength, atIndex: 0)
}
default:
FFAReportUtil.report(.Error, message: "\(lengthWithEmoji) is invalid")
}
if lengthWithEmoji == emojiVisibleLength {
break
}
}
return possibleLengths.last!
}
func truncateWithEmoji(length: Int, trailing: String? = "...") -> String {
if emojiVisibleLength > length {
let lengthWithEmoji = calculateLengthWithEmoji(length)
return self[startIndex..<startIndex.advancedBy(lengthWithEmoji)] + trailing!
} else {
return self
}
}
}
emojiVisibleLength
という変数は絵文字を含む文字列の場合でも見た目の文字数を取得できるプロパティです。
"👨👩👧👦".characters.count //4
"👨👩👧".characters.count //3
"🐧".characters.count //1
"🇺🇸🇺🇸".characters.count //1
"👨👩👧👦".emojiVisibleLength //1
"👨👩👧".emojiVisibleLength //2 <- errorです
"🐧".emojiVisibleLength //1
"🇺🇸🇺🇸".emojiVisibleLength //2
絵文字は見た目は1文字なのにcharacters.count
では違う値をとる場合があります。
そこで、emojiVisibleLengthという変数は絵文字を含む文字列の場合でも見た目の文字数を取得できるプロパティです(ただし、上記のようなerrorも含んでいます。修正したい。)。
しかし、絵文字を含む文字列をはじめから120字だけ抜き出して表示させたい時に困りました。
見た目で120字目がcharacters.count
で言うところの何文字目のことなのかわからないので、見た目で120字表示させたい時にindexを指定することができないのです。。。
例えば、
var test = "hog🐧👨👩👧👦eh🐧g👨👩👧👦ahkk🐧sjf"
を考えてみましょう。
// "hog🐧👨👩👧👦"が欲しい
test[test.startIndex..<test.startIndex.advancedBy(5)] // "hog🐧👨"
なんか知らん奴が登場してきました!!!!!!!!!!!!
誰だ、このboyは!!!!!!!!!!!!!!!!
indexを一つ増やしてみましょう!
// "hog🐧👨👩👧👦e"が欲しい
test[test.startIndex..<test.startIndex.advancedBy(6)] // "hog🐧👨👩"
ほ!!!
家族がバラバラにされています!!!!!!!!!
ということは、この流れだと7にすると父親が登場するかと思い、7にしたら、、、、
// "hog🐧👨👩👧👦eh"が欲しい
test[test.startIndex..<test.startIndex.advancedBy(7)] // "hog🐧👨👩👧"
ふあ!!!!!!!!!
予想に反して家族が合流しました!!!!!!
ん、しかし、よく見ると、、、
あれ、子供がいない????!!!!!!!!!
この家族に何が起きたんdaaaaaaaaaaaaaaaaaaaaaa!!??????????
などと、混乱しました。
ちなみに続きはこんな感じ。
test[test.startIndex..<test.startIndex.advancedBy(8)] // "hog🐧👨👩👧👦"
test[test.startIndex..<test.startIndex.advancedBy(9)] // "hog🐧👨👩👧👦e"
8にするとようやく家族が元どおりになりました!!!
このように絵文字が入ることで、見た目での文字列の数とindexが全然合わなくなってしまいます。
そこで、「抜き出したい文字数を指定したら、その数だけ見た目上の文字数で抜き出した文字列のcharacters.countを算出する関数」を作成しました。
それがfunc calculateLengthWithEmoji(max: Int) -> Int
です。
これはまだ未完成なのでご意見、ご提案ありましたら教えてください。
test.calculateLengthWithEmoji(5) //8
test.calculateLengthWithEmoji(6) //9
test.calculateLengthWithEmoji(7) //10
test.calculateLengthWithEmoji(8) //11
test.calculateLengthWithEmoji(9) //12
次に、上記で算出したcharacters.countを引数に抜き出した文字列の末尾のindexを取得することで、指定した文字数分だけ見た目上表示させる関数を作成しました。
それがfunc truncateWithEmoji(length: Int, trailing: String? = "...") -> String
です。
test.truncateEmojiVisible(5) //"hog🐧👨👩👧👦..."
test.truncateEmojiVisible(6) //"hog🐧👨👩👧👦e..."
test.truncateEmojiVisible(7) //"hog🐧👨👩👧👦eh..."
test.truncateEmojiVisible(8) //"hog🐧👨👩👧👦eh🐧..."
test.truncateEmojiVisible(9) //"hog🐧👨👩👧👦eh🐧g..."
以上です。
開発者の思いやりが足らないせいで、知らないところで家族が一家離散してしまう、ということを実感しました。
みなさんのアプリでも同様の事件が起こらないように祈ります。
まだ改良中ですが、どなたかの参考になりましたら幸いです。
Author And Source
この問題について(絵文字を含む文字列を見た目の文字数で評価して家族を守る記事), 我々は、より多くの情報をここで見つけました https://qiita.com/shunsukekyuwa/items/1a22d3a5d6871dc545aa著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .