Les String
gammes et NSString
gammes Swift ne sont pas "compatibles". Par exemple, un emoji comme 😄 compte pour un caractère Swift, mais comme deux NSString
caractères (une paire de substituts UTF-16).
Par conséquent, votre solution suggérée produira des résultats inattendus si la chaîne contient de tels caractères. Exemple:
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)
Production:
😄😄😄Long paragra {
} ph dis {
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} ing! {
}
Comme vous le voyez, "ph say" a été marqué avec l'attribut, pas "dire".
Étant donné que NS(Mutable)AttributedString
nécessite finalement un NSString
et un NSRange
, il est en fait préférable de convertir la chaîne donnée en NSString
premier. Ensuite, le substringRange
est un NSRange
et vous n'avez plus à convertir les plages:
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)
Production:
😄😄😄Long paragraphe {
}en disant{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}
Mise à jour pour 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)
Mise à jour pour 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)
Mise à jour pour Swift 4:
Depuis Swift 4 (Xcode 9), la bibliothèque standard Swift fournit une méthode pour convertir entre Range<String.Index>
et NSRange
. La conversion en NSString
n'est plus nécessaire:
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)
Voici substringRange
un Range<String.Index>
, et qui est converti en le correspondant NSRange
avec
NSRange(substringRange, in: text)