Szybkie String
zakresy i NSString
zakresy nie są „zgodne”. Na przykład emoji, takie jak 😄, liczy się jako jeden znak Swift, ale jako dwa NSString
znaki (tak zwana para zastępcza UTF-16).
Dlatego sugerowane rozwiązanie przyniesie nieoczekiwane wyniki, jeśli ciąg zawiera takie znaki. Przykład:
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)
Wynik:
😄😄😄Long paragra {
} ph say {
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} ing! {
}
Jak widać, „ph say” zostało oznaczone atrybutem, a nie „mówiąc”.
Ponieważ NS(Mutable)AttributedString
ostatecznie wymaga an NSString
i NSRange
, w rzeczywistości lepiej jest przekonwertować dany ciąg na NSString
pierwszy. Wtedy substringRange
jest an NSRange
i nie musisz już konwertować zakresów:
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)
Wynik:
😄😄😄Długi akapit {
}powiedzenie{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}
Aktualizacja dla 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)
Aktualizacja dla 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)
Aktualizacja dla Swift 4:
Począwszy od Swift 4 (Xcode 9), standardowa biblioteka Swift zapewnia metodę konwersji między Range<String.Index>
i NSRange
. Konwersja na NSString
nie jest już konieczna:
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)
Tutaj substringRange
jest Range<String.Index>
, i to jest konwertowane na odpowiadające NSRange
z
NSRange(substringRange, in: text)