Schnelle String
Bereiche und NSString
Bereiche 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)AttributedString
letztendlich ein NSString
und ein erforderlich ist NSRange
, ist es tatsächlich besser, den angegebenen String NSString
zuerst in einen zu konvertieren . Dann substringRange
ist das ein NSRange
und 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 NSString
ist 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 substringRange
ist ein Range<String.Index>
, und das wird in das entsprechende NSRange
mit konvertiert
NSRange(substringRange, in: text)