键盘扩展调整表情符号的文本位置问题
KeyboardExtension adjustTextPosition issues with emojis
我正在帮助构建一个键盘扩展,我最近 运行 遇到了 Swift 4 和表情符号的问题。 Swift 4 的新 UTF-16 表情符号支持非常好,但 UIInputViewController
中的 adjustTextPosition
存在问题。
如果我们调用 adjustTextPosition
跨过一个表情符号,它只会跨得不够远,似乎 UIInputViewController
使用的字符偏移量与系统使用的字符数不匹配。
要测试简单地写一个带有表情符号的文本,每当点击某个键时调用:
super.textDocumentProxy.adjustTextPosition(byCharacterOffset: 1)
可以观察到我们点击它的次数超过了预期。
Swift 5、看来下面的代码在iOS 12.
上运行良好
let count: Int = String(text).utf16.count
textDocumentProxy.adjustTextPosition(byCharacterOffset: count)
调整字素簇(Swift 个字符)中测量的插入符位置:
func adjustCaretPosition(offset: Int) {
guard let textAfterCaret = textDocumentProxy.documentContextAfterInput else { return }
if let offsetIndex = offset > 0 ? textAfterCaret.index(textAfterCaret.startIndex, offsetBy: offset, limitedBy: textAfterCaret.endIndex) : textBeforeCaret.index(textBeforeCaret.endIndex, offsetBy: offset, limitedBy: textAfterCaret.startIndex),
let offsetIndex_utf16 = offsetIndex.samePosition(in: offset > 0 ? textAfterCaret.utf16 : textBeforeCaret.utf16)
{
let offset = offset > 0 ? textAfterCaret.utf16.distance(from: textAfterCaret.utf16.startIndex, to: offsetIndex_utf16) : textBeforeCaret.utf16.distance(from: textBeforeCaret.utf16.endIndex, to: offsetIndex_utf16)
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
else {
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
}
UPD:一个混乱的 hack,试图修复 Safari 的不一致。由于无法区分 safari 和非 safari,因此根据结果采取行动似乎是唯一的解决方案。
func adjustCaretPosition(offset: Int) {
// for convenience
let textAfterCaret = textDocumentProxy.documentContextAfterInput ?? ""
let textBeforeCaret = textDocumentProxy.documentContextBeforeInput ?? ""
if let offsetIndex = offset > 0 ? textAfterCaret.index(textAfterCaret.startIndex, offsetBy: offset, limitedBy: textAfterCaret.endIndex) : textBeforeCaret.index(textBeforeCaret.endIndex, offsetBy: offset, limitedBy: textAfterCaret.startIndex),
let offsetIndex_utf16 = offsetIndex.samePosition(in: offset > 0 ? textAfterCaret.utf16 : textBeforeCaret.utf16)
{
// part of context before caret adjustment
let previousText = offset > 0 ? textAfterCaret : textBeforeCaret
// what we expect after adjustment
let expectedText = offset > 0 ? String(textAfterCaret[offsetIndex..<textAfterCaret.endIndex]) : String(textBeforeCaret[textBeforeCaret.startIndex..<offsetIndex])
// offset in UTF-16 characters
let offset_utf16 = offset > 0 ? textAfterCaret.utf16.distance(from: textAfterCaret.utf16.startIndex, to: offsetIndex_utf16) : textBeforeCaret.utf16.distance(from: textBeforeCaret.utf16.endIndex, to: offsetIndex_utf16)
// making adjustment
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
// part of context after caret adjustment
let compareText = offset > 0 ? textAfterCaret : textBeforeCaret
// rollback if got unwanted results
// then adjust by grapheme clusters offset
if compareText != "", expectedText != compareText, compareText != previousText {
textDocumentProxy.adjustTextPosition(byCharacterOffset: -offset_utf16)
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
}
else {
// we probably stumbled upon a textDocumentProxy inconsistency, i.e. context got divided by an emoji
// adjust by grapheme clusters offset
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
}
试试这个
let correctedOffset = adjust(offset: offset)
textDocumentProxy.adjustTextPosition(byCharacterOffset: correctedOffset)
private func adjust(offset: Int) -> Int {
if offset > 0, let after = textDocumentProxy.documentContextAfterInput {
let offsetStringIndex = after.index(after.startIndex, offsetBy: offset)
let chunk = after[..<offsetStringIndex]
let characterCount = chunk.utf16.count
return characterCount
} else if offset < 0, let before = textDocumentProxy.documentContextBeforeInput {
let offsetStringIndex = before.index(before.endIndex, offsetBy: offset)
let chunk = before[offsetStringIndex...]
let characterCount = chunk.utf16.count
return -1*characterCount
} else {
return offset
}
}
我正在帮助构建一个键盘扩展,我最近 运行 遇到了 Swift 4 和表情符号的问题。 Swift 4 的新 UTF-16 表情符号支持非常好,但 UIInputViewController
中的 adjustTextPosition
存在问题。
如果我们调用 adjustTextPosition
跨过一个表情符号,它只会跨得不够远,似乎 UIInputViewController
使用的字符偏移量与系统使用的字符数不匹配。
要测试简单地写一个带有表情符号的文本,每当点击某个键时调用:
super.textDocumentProxy.adjustTextPosition(byCharacterOffset: 1)
可以观察到我们点击它的次数超过了预期。
Swift 5、看来下面的代码在iOS 12.
上运行良好let count: Int = String(text).utf16.count
textDocumentProxy.adjustTextPosition(byCharacterOffset: count)
调整字素簇(Swift 个字符)中测量的插入符位置:
func adjustCaretPosition(offset: Int) {
guard let textAfterCaret = textDocumentProxy.documentContextAfterInput else { return }
if let offsetIndex = offset > 0 ? textAfterCaret.index(textAfterCaret.startIndex, offsetBy: offset, limitedBy: textAfterCaret.endIndex) : textBeforeCaret.index(textBeforeCaret.endIndex, offsetBy: offset, limitedBy: textAfterCaret.startIndex),
let offsetIndex_utf16 = offsetIndex.samePosition(in: offset > 0 ? textAfterCaret.utf16 : textBeforeCaret.utf16)
{
let offset = offset > 0 ? textAfterCaret.utf16.distance(from: textAfterCaret.utf16.startIndex, to: offsetIndex_utf16) : textBeforeCaret.utf16.distance(from: textBeforeCaret.utf16.endIndex, to: offsetIndex_utf16)
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
else {
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
}
UPD:一个混乱的 hack,试图修复 Safari 的不一致。由于无法区分 safari 和非 safari,因此根据结果采取行动似乎是唯一的解决方案。
func adjustCaretPosition(offset: Int) {
// for convenience
let textAfterCaret = textDocumentProxy.documentContextAfterInput ?? ""
let textBeforeCaret = textDocumentProxy.documentContextBeforeInput ?? ""
if let offsetIndex = offset > 0 ? textAfterCaret.index(textAfterCaret.startIndex, offsetBy: offset, limitedBy: textAfterCaret.endIndex) : textBeforeCaret.index(textBeforeCaret.endIndex, offsetBy: offset, limitedBy: textAfterCaret.startIndex),
let offsetIndex_utf16 = offsetIndex.samePosition(in: offset > 0 ? textAfterCaret.utf16 : textBeforeCaret.utf16)
{
// part of context before caret adjustment
let previousText = offset > 0 ? textAfterCaret : textBeforeCaret
// what we expect after adjustment
let expectedText = offset > 0 ? String(textAfterCaret[offsetIndex..<textAfterCaret.endIndex]) : String(textBeforeCaret[textBeforeCaret.startIndex..<offsetIndex])
// offset in UTF-16 characters
let offset_utf16 = offset > 0 ? textAfterCaret.utf16.distance(from: textAfterCaret.utf16.startIndex, to: offsetIndex_utf16) : textBeforeCaret.utf16.distance(from: textBeforeCaret.utf16.endIndex, to: offsetIndex_utf16)
// making adjustment
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
// part of context after caret adjustment
let compareText = offset > 0 ? textAfterCaret : textBeforeCaret
// rollback if got unwanted results
// then adjust by grapheme clusters offset
if compareText != "", expectedText != compareText, compareText != previousText {
textDocumentProxy.adjustTextPosition(byCharacterOffset: -offset_utf16)
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
}
else {
// we probably stumbled upon a textDocumentProxy inconsistency, i.e. context got divided by an emoji
// adjust by grapheme clusters offset
textDocumentProxy.adjustTextPosition(byCharacterOffset: offset)
}
}
试试这个
let correctedOffset = adjust(offset: offset)
textDocumentProxy.adjustTextPosition(byCharacterOffset: correctedOffset)
private func adjust(offset: Int) -> Int {
if offset > 0, let after = textDocumentProxy.documentContextAfterInput {
let offsetStringIndex = after.index(after.startIndex, offsetBy: offset)
let chunk = after[..<offsetStringIndex]
let characterCount = chunk.utf16.count
return characterCount
} else if offset < 0, let before = textDocumentProxy.documentContextBeforeInput {
let offsetStringIndex = before.index(before.endIndex, offsetBy: offset)
let chunk = before[offsetStringIndex...]
let characterCount = chunk.utf16.count
return -1*characterCount
} else {
return offset
}
}