Быстрые String
диапазоны и NSString
диапазоны не являются «совместимыми». Например, смайлик типа 😄 считается одним символом Свифта, но двумя NSString
символами (так называемая суррогатная пара UTF-16).
Поэтому предложенное вами решение даст неожиданные результаты, если строка содержит такие символы. Пример:
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)
Вывод:
ParaДлительная парагра {
} ph сказать {
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
} ИНГ! {
}
Как видите, «ph say» был помечен атрибутом, а не «say».
Так как в NS(Mutable)AttributedString
конечном итоге требуются an NSString
и an NSRange
, на самом деле лучше преобразовать данную строку в NSString
первую. Тогда substringRange
это NSRange
и вам больше не нужно конвертировать диапазоны:
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)
Вывод:
ParagraphДлинный абзац {
} {Говоря
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}! {
}
Обновление для 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)
Обновление для 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)
Обновление для Swift 4:
Начиная с Swift 4 (Xcode 9), стандартная библиотека Swift предоставляет метод для преобразования между Range<String.Index>
и NSRange
. Преобразование в NSString
больше не нужно:
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)
Вот substringRange
это Range<String.Index>
, и что превращают в соответствующее NSRange
с
NSRange(substringRange, in: text)