一尘不染

Swift Range的NSRange?

swift

问题: 我在使用使用Range的Swift String时,NSAttributedString需要一个NSRange

let text = "Long paragraph saying something goes here!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})

产生以下错误:

错误:“范围”不能转换为“ NSRange”
attributedString.addAttribute(NSForegroundColorAttributeName,值:NSColor.redColor(),范围:substringRange)


阅读 340

收藏
2020-07-07

共1个答案

一尘不染

快速String范围和NSString范围不是“兼容的”。例如,像😄这样的表情符号算作一个Swift字符,但算作两个NSString
字符(所谓的UTF-16代理对)。

因此,如果字符串包含此类字符,则建议的解决方案将产生意外结果。例:

let text = "😄😄😄Long paragraph saying!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
    let start = distance(text.startIndex, substringRange.startIndex)
    let length = distance(substringRange.startIndex, substringRange.endIndex)
    let range = NSMakeRange(start, length)

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: range)
    }
})
println(attributedString)

输出:

para长paragra {
} ph说{
    NSColor =“ NSCalibratedRGBColorSpace 1 0 0 1”;
} ing!{
}

如您所见,“ ph say”已被标记为属性,而不是“ saying”。

由于NS(Mutable)AttributedString最终需要使用NSStringand和an
NSRange,因此最好将给定的字符串转换为NSStringfirst。然后substringRange
NSRange,则您不再需要转换范围:

let text = "😄😄😄Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: nsText)

nsText.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})
println(attributedString)

输出:

paragraph长款{
}说{
    NSColor =“ NSCalibratedRGBColorSpace 1 0 0 1”;
}!{
}

Swift 2更新:

let text = "😄😄😄Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstringsInRange(textRange, options: .ByWords, usingBlock: {
    (substring, substringRange, _, _) in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
    }
})
print(attributedString)

Swift 3更新:

let text = "😄😄😄Long paragraph saying!"
let nsText = text as NSString
let textRange = NSMakeRange(0, nsText.length)
let attributedString = NSMutableAttributedString(string: text)

nsText.enumerateSubstrings(in: textRange, options: .byWords, using: {
    (substring, substringRange, _, _) in

    if (substring == "saying") {
        attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.red, range: substringRange)
    }
})
print(attributedString)

Swift 4更新:

从Swift 4(Xcode
9)开始,Swift标准库提供了在Range<String.Index>和之间进行转换的方法NSRangeNSString不再需要转换为:

let text = "😄😄😄Long paragraph saying!"
let attributedString = NSMutableAttributedString(string: text)

text.enumerateSubstrings(in: text.startIndex..<text.endIndex, options: .byWords) {
    (substring, substringRange, _, _) in
    if substring == "saying" {
        attributedString.addAttribute(.foregroundColor, value: NSColor.red,
                                      range: NSRange(substringRange, in: text))
    }
}
print(attributedString)

substringRange是一个Range<String.Index>,并将其转换为NSRange

NSRange(substringRange, in: text)
2020-07-07