Schnelle StringBereiche und NSStringBereiche sind nicht "kompatibel". Zum Beispiel zählt ein Emoji wie 😄 als ein Swift-Zeichen, aber als zwei NSString
Zeichen (ein sogenanntes UTF-16-Ersatzpaar).
Daher führt Ihre vorgeschlagene Lösung zu unerwarteten Ergebnissen, wenn die Zeichenfolge solche Zeichen enthält. Beispiel:
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)
Ausgabe:
😄😄😄Langes Paragra {
} ph say {
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} ing! {
}}
Wie Sie sehen, wurde "ph say" mit dem Attribut markiert, nicht mit "say".
Da NS(Mutable)AttributedStringletztendlich ein NSStringund ein erforderlich ist NSRange, ist es tatsächlich besser, den angegebenen String NSStringzuerst in einen zu konvertieren . Dann substringRange
ist das ein NSRangeund Sie müssen die Bereiche nicht mehr konvertieren:
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)
Ausgabe:
😄😄😄Langer Absatz {
}Sprichwort{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}}
Update für 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)
Update für 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)
Update für Swift 4:
Ab Swift 4 (Xcode 9) bietet die Swift-Standardbibliothek eine Methode zum Konvertieren zwischen Range<String.Index>und NSRange. Eine Umstellung auf NSStringist nicht mehr erforderlich:
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)
Hier substringRangeist ein Range<String.Index>, und das wird in das entsprechende NSRangemit konvertiert
NSRange(substringRange, in: text)