Problem: NSAttributedString takes an NSRange while I'm using a Swift String that uses Range
let text = "Long paragraph saying something goes here!"
let textRange = text.startIndex..<text.endIndex
let attributedString = NSMutableAttributedString(string: text)
text.enumerateSubstringsInRange(textRange, options: NSStringEnumerationOptions.ByWords, { (substring, substringRange, enclosingRange, stop) -> () in
if (substring == "saying") {
attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
}
})
Produces the following error:
error: 'Range' is not convertible to 'NSRange' attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange)
Swift
String
ranges andNSString
ranges are not "compatible". For example, an emoji like counts as one Swift character, but as twoNSString
characters (a so-called UTF-16 surrogate pair).Therefore your suggested solution will produce unexpected results if the string contains such characters. Example:
Output:
As you see, "ph say" has been marked with the attribute, not "saying".
Since
NS(Mutable)AttributedString
ultimately requires anNSString
and anNSRange
, it is actually better to convert the given string toNSString
first. Then thesubstringRange
is anNSRange
and you don't have to convert the ranges anymore:Output:
Update for Swift 2:
Update for Swift 3:
Update for Swift 4:
As of Swift 4 (Xcode 9), the Swift standard library provides method to convert between
Range<String.Index>
andNSRange
. Converting toNSString
is no longer necessary:Here
substringRange
is aRange<String.Index>
, and that is converted to the correspondingNSRange
with