Le String
gamme veloci e le NSString
gamme non sono "compatibili". Ad esempio, un'emoji come 😄 conta come un personaggio Swift, ma come due NSString
personaggi (una cosiddetta coppia surrogata UTF-16).
Pertanto la soluzione suggerita produrrà risultati imprevisti se la stringa contiene tali caratteri. Esempio:
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)
Produzione:
😄😄😄Long paragrafo {
} ph dire {
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} Ing! {
}
Come vedi, "ph dire" è stato contrassegnato con l'attributo, non "dire".
Poiché alla NS(Mutable)AttributedString
fine richiede an NSString
e an NSRange
, in realtà è meglio convertire prima la stringa data NSString
. Quindi substringRange
è un NSRange
e non devi più convertire gli intervalli:
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)
Produzione:
😄😄😄Lungo paragrafo {
}detto{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}
Aggiornamento per 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)
Aggiornamento per 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)
Aggiornamento per Swift 4:
A partire da Swift 4 (Xcode 9), la libreria standard Swift fornisce il metodo per convertire tra Range<String.Index>
e NSRange
. La conversione in NSString
non è più necessaria:
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)
Ecco substringRange
un Range<String.Index>
, e che viene convertito nel corrispondente NSRange
con
NSRange(substringRange, in: text)