Os String
intervalos e os NSString
intervalos rápidos não são "compatíveis". Por exemplo, um emoji como 😄 conta como um caractere Swift, mas como dois NSString
caracteres (o chamado par substituto UTF-16).
Portanto, sua solução sugerida produzirá resultados inesperados se a sequência contiver esses caracteres. Exemplo:
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)
Resultado:
ParLonga paragra {
} ph diz {
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} ing! {
}
Como você vê, "ph say" foi marcado com o atributo, não "dizendo".
Como, em NS(Mutable)AttributedString
última análise, requer um NSString
e um NSRange
, é realmente melhor converter a sequência fornecida em NSString
primeiro. Então o substringRange
é um NSRange
e você não precisa mais converter os intervalos:
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)
Resultado:
Paragraph Parágrafo longo {
}dizendo{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}
Atualização para o 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)
Atualização para o 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)
Atualização para o Swift 4:
A partir do Swift 4 (Xcode 9), a biblioteca padrão do Swift fornece um método para converter entre Range<String.Index>
e NSRange
. A conversão para NSString
não é mais necessária:
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)
Aqui substringRange
está um Range<String.Index>
, e que é convertido no correspondente NSRange
com
NSRange(substringRange, in: text)