Swift:NSAttributedString和表情符号

8

我使用启用了文本属性编辑的UITextView,在文本中含有表情符号时,获取属性存在问题。

这是我使用的代码:

var textAttributes = [(attributes: [NSAttributedString.Key: Any], range: NSRange)]()
let range = NSRange(location: 0, length: textView.attributedText.length)
textView.attributedText.enumerateAttributes(in: range) { dict, range, _ in
    textAttributes.append((attributes: dict, range: range))
}

for attribute in textAttributes {
    if let swiftRange = Range(attribute.range, in: textView.text) {
        print("NSRange \(attribute.range): \(textView.text![swiftRange])")
    } else {
        print("NSRange \(attribute.range): cannot convert to Swift range")
    }
}

当我使用"Sample text ❤️"这样的文本进行尝试时,输出如下:
NSRange {0, 12}: Sample text NSRange {12, 1}: 无法转换为Swift范围 NSRange {13, 1}: 无法转换为Swift范围
所以你可以看到,我无法获取带有表情符号的文本。
文本属性由我自定义的NSTextStorage应用于文本视图来设置。这是setAttributes方法:
override func setAttributes(_ attrs: [NSAttributedString.Key: Any]?, range: NSRange) {
    guard (range.location + range.length - 1) < string.count  else {
        print("Range out of bounds")
        return
    }

    beginEditing()
    storage.setAttributes(attrs, range: range)
    edited(.editedAttributes, range: range, changeInLength: 0)
    endEditing()
}

请注意,在编辑我的文本视图时,我会看到一些“范围超出界限”的打印信息。
是否有一种方法将NSRange转换为有效的Swift Range?

“有没有一种方法可以将NSRange转换为有效的Swift Range?” - 您可以通过 Range(attribute.range, in: textView.text) 进行转换。 - rmaddy
1
需要将 guard (range.location + range.length - 1) < string.count else { 修改为 guard (range.location + range.length - 1) < string.utf16.count else { - rmaddy
@rmaddy 谢谢,string.utf16.count 很有效!但是,转换为 Swift Range 仍然会显示“无法转换为 Swift 范围”。我成功地使用了 textView.attributedText.attributedSubstring(from: attribute.range).string 来进行子字符串操作。 - Florentin
1
你的某个地方可能有一个基于Swift String计数的NSRange,但是这是错误的。在从String创建NSRange时,它必须始终为.utf16.count。因此,请再次展示如何将属性应用于属性文本(并展示如何创建范围)。 - rmaddy
非常感谢,我已经将所有的.count改为了.utf16.countNSRange中,现在它按预期工作了! - Florentin
1个回答

20
使用 NSAttributedStringNSRangeString 时最重要的一点是要记住,NSAttributedString(和 NSString)以及 NSRange 是基于 UTF-16 编码长度计算的。而 String 及其 count 是基于实际字符数计算的。它们不混合使用。
如果你试图使用 someSwiftString.count 创建一个 NSRange,你会得到错误的范围。请始终使用 someSwiftString.utf16.count
在你的特定情况下,由于 NSRange 中长度错误,你正在将属性应用于 ❤️ 字符的一半,这导致了你看到的错误。
在你发布的代码中,你需要更改:
guard (range.location + range.length - 1) < string.count else {

至:

guard (range.location + range.length - 1) < string.utf16.count else {

出于上述相同的原因。


太好了!它今天帮助了我。 - JohnnL

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接