快速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>和之间进行转换的方法NSRange。NSString不再需要转换为:
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)