Swift String
диапазонов и NSString
диапазонов не "совместимы". Например, подобный эмози считается одним символом Swift, но как два символа 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)
Выход:
Long paragra{
}ph say{
NSColor = "NSCalibratedRGBColorSpace 1 0 0 1";
}ing!{
}
Как вы видите, "говорят фот", который был отмечен атрибутом, а не "говорить".
Поскольку 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)
Выход:
Long paragraph {
}saying{
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:
В Свифта 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)
Возможный дубликат [NSRange ранжировать] (http://stackoverflow.com/questions/25138339/nsrange-to-rangestring -index) –
Suhaib