公司有一个表情键盘的需求,在输入完表情发送时需要将表情转化为文字与服务端交互。 于是我写了一个UITextView的Category来进行转换
@objc extension UITextView {
@objc public var plainText: String {
var string = self.attributedText.string
let originCount = string.count
self.attributedText.enumerateAttribute(NSAttributedString.Key.attachment, in: NSRange(location: 0, length: self.attributedText.length), options: []) { (value, range, _) in
if let attachment = value as? EmojiTextAttachment {
let startIndex = string.index(string.endIndex, offsetBy: -(originCount - range.location))
let endIndex = string.index(string.endIndex, offsetBy: -(originCount - range.location - 1))
string.replaceSubrange(startIndex ..< endIndex, with: attachment.textInput)
}
}
return string
}
}
后来测试说输入emoji的情况下会闪退,仔细观察了一遍代码,想到了在swift中使用string.count获得的字符数会比OC中少,例如“😁😁😁笑笑笑”这样一段字符,使用string.count的出来的是6,而NSAttributeString.length获得的却是8,这就导致在字符串末尾处的startIndex越界了。
由于之前遇到过类似的情况,知道string还提供了string.utf16.count来获得正确字符数,于是稍作修改,运行一遍发现又闪退了...
做了一点测试后发现,虽然originCount已经取得了正确字符数,但是string.endIndex仍然是使用的swift的计数方式,于是在字符串的开头处startIndex就越界了。
最后也没有想到什么很好的办法,只能通过NSString来进行文本替换,最终的代码如下:
@objc extension UITextView {
@objc public var plainText: String {
var string = NSString.init(string:self.attributedText.string)
let originCount = string.length
self.attributedText.enumerateAttribute(NSAttributedString.Key.attachment, in: NSRange(location: 0, length: self.attributedText.length), options: []) { (value, range, _) in
if let attachment = value as? EmojiTextAttachment {
let replaceRange = NSRange(location: string.length - ( originCount - range.location), length: 1)
string = string.replacingCharacters(in: replaceRange, with: attachment.textInput) as NSString
}
}
return string as String
}
}
这样最终也是可以解决崩溃。 swift中关于emoji的处理可以看这篇文章:Swift 4 中的字符串