Los String
rangos rápidos y los NSString
rangos 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 NSString
y una NSRange
, en realidad es mejor convertir NSString
primero la cadena dada . Entonces el substringRange
es un NSRange
y 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 NSString
ya 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í substringRange
hay un Range<String.Index>
, y que se convierte al correspondiente NSRange
con
NSRange(substringRange, in: text)