问题: 我在使用使用Range的Swift String时,NSAttributedString需要一个NSRange
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) } })
产生以下错误:
错误:“范围”不能转换为“ NSRange” attributedString.addAttribute(NSForegroundColorAttributeName,值:NSColor.redColor(),范围:substringRange)
快速String范围和NSString范围不是“兼容的”。例如,像😄这样的表情符号算作一个Swift字符,但算作两个NSString 字符(所谓的UTF-16代理对)。
String
NSString
因此,如果字符串包含此类字符,则建议的解决方案将产生意外结果。例:
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长paragra { } ph说{ NSColor =“ NSCalibratedRGBColorSpace 1 0 0 1”; } ing!{ }
如您所见,“ ph say”已被标记为属性,而不是“ saying”。
由于NS(Mutable)AttributedString最终需要使用NSStringand和an NSRange,因此最好将给定的字符串转换为NSStringfirst。然后substringRange 是NSRange,则您不再需要转换范围:
NS(Mutable)AttributedString
NSRange
substringRange
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不再需要转换为:
Range<String.Index>
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)