Los Stringrangos rápidos y los NSStringrangos no son "compatibles". Por ejemplo, un emoji como 😄 cuenta como un personaje Swift, pero como dos NSString
caracteres (un par sustituto llamado UTF-16).
Por lo tanto, su solución sugerida producirá resultados inesperados si la cadena contiene dichos caracteres. Ejemplo:
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)
Salida:
😄😄😄Paragra larga {
} ph decir {
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}¡En g!{
}
Como puede ver, "ph say" ha sido marcado con el atributo, no "diciendo".
Como en NS(Mutable)AttributedStringúltima instancia requiere una NSStringy una NSRange, en realidad es mejor convertir NSStringprimero la cadena dada . Entonces el substringRange
es un NSRangey ya no tienes que convertir los rangos:
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)
Salida:
😄😄😄Párrafo largo {
}diciendo{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}
Actualización para 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)
Actualización para 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)
Actualización para Swift 4:
A partir de Swift 4 (Xcode 9), la biblioteca estándar de Swift proporciona un método para convertir entre Range<String.Index>y NSRange. La conversión a NSStringya no es necesaria:
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)
Aquí substringRangehay un Range<String.Index>, y que se convierte al correspondiente NSRangecon
NSRange(substringRange, in: text)