Швидкі 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";
} ing! {
}
Як бачите, "атрибут ph" позначений атрибутом, а не "кажучи".
Оскільки в NS(Mutable)AttributedString
кінцевому рахунку потрібні і NSString
і 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)