来自Swift Range的NSRange?

问题:当我使用一个使用Range的Swiftstring时,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) } }) 

产生以下错误:

错误:'Range'不能转换为'NSRange'属性string.addAttribute(NSForegroundColorAttributeName,值:NSColor.redColor(),范围:substringRange)

Swift String范围和NSString范围不是“兼容的”。 例如,像😄这样的表情符号就是一个Swift字符,而是两个NSString字符(一个所谓的UTF-16代理对)。

因此,如果string包含这样的字符,则build议的解决scheme将产生意外的结果 例:

 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) 

输出:

 😄😄😄长时间的{
 } ph说{
     NSColor =“NSCalibratedRGBColorSpace 1 0 0 1”;
 } ING!{
 }

正如你所看到的,“ph说”已经标记了属性,而不是“说”。

由于NS(Mutable)AttributedString最终需要一个NSString和一个NSRange ,实际上最好先将给定的string转换为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) 

输出:

 😄😄😄长款{
 } {说法
     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,目前处于testing阶段)开始,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) 

对于你所描述的情况,我发现这个工作。 它相对短而甜美:

  let attributedString = NSMutableAttributedString(string: "follow the yellow brick road") //can essentially come from a textField.text as well (will need to unwrap though) let text = "follow the yellow brick road" let str = NSString(string: text) let theRange = str.rangeOfString("yellow") attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellowColor(), range: theRange) 

可能的scheme

Swift提供了可以用来创build一个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 let start = distance(text.startIndex, substringRange.startIndex) let length = distance(substringRange.startIndex, substringRange.endIndex) let range = NSMakeRange(start, length) // println("word: \(substring) - \(d1) to \(d2)") if (substring == "saying") { attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: range) } }) 

答案是好的,但用Swift 4你可以简化你的代码:

 let text = "Test string" let substring = "string" let substringRange = text.range(of: substring)! let nsRange = NSRange(substringRange, in: text) 

请注意, rangefunction的结果必须解开。

Swift 4:

当然,我知道Swift 4已经为NSRange扩展了

 public init<R, S>(_ region: R, in target: S) where R : RangeExpression, S : StringProtocol, R.Bound == String.Index, S.Index == String.Index 

我知道在大多数情况下,这个init就足够了。 看它的用法:

 let string = "Many animals here: 🐶🦇🐱 !!!" if let range = string.range(of: "🐶🦇🐱"){ print((string as NSString).substring(with: NSRange(range, in: string))) // "🐶🦇🐱" } 

但是转换可以直接从Range <String.Index>到NSRange而不用Swift的String实例。

而不是通用的init用法,它要求你的目标参数为String ,如果你没有目标string,你可以直接创build转换

 extension NSRange { public init(_ range:Range<String.Index>) { self.init(location: range.lowerBound.encodedOffset, length: range.upperBound.encodedOffset - range.lowerBound.encodedOffset) } } 

或者你可以为范围本身创build专门的扩展

 extension Range where Bound == String.Index { var nsRange:NSRange { return NSRange(location: self.lowerBound.encodedOffset, length: self.upperBound.encodedOffset - self.lowerBound.encodedOffset) } } 

用法:

 let string = "Many animals here: 🐶🦇🐱 !!!" if let range = string.range(of: "🐶🦇🐱"){ print((string as NSString).substring(with: NSRange(range))) // "🐶🦇🐱" } 

要么

 if let nsrange = string.range(of: "🐶🦇🐱")?.nsRange{ print((string as NSString).substring(with: nsrange)) // "🐶🦇🐱" } 
 func formatAttributedStringWithHighlights(text: String, highlightedSubString: String?, formattingAttributes: [String: AnyObject]) -> NSAttributedString { let mutableString = NSMutableAttributedString(string: text) let text = text as NSString // convert to NSString be we need NSRange if let highlightedSubString = highlightedSubString { let highlightedSubStringRange = text.rangeOfString(highlightedSubString) // find first occurence if highlightedSubStringRange.length > 0 { // check for not found mutableString.setAttributes(formattingAttributes, range: highlightedSubStringRange) } } return mutableString } 

我喜欢Swift语言,但是使用与NSRange不兼容的Swift Range NSRange让我头痛得太久了。 所以为了解决所有这些垃圾,我devise了下面的方法来返回一个NSMutableAttributedString ,并用你的颜色设置突出显示的单词。

这对emojis不起作用。 如果你必须修改。

 extension String { func getRanges(of string: String) -> [NSRange] { var ranges:[NSRange] = [] if contains(string) { let words = self.components(separatedBy: " ") var position:Int = 0 for word in words { if word.lowercased() == string.lowercased() { let startIndex = position let endIndex = word.characters.count let range = NSMakeRange(startIndex, endIndex) ranges.append(range) } position += (word.characters.count + 1) // +1 for space } } return ranges } func highlight(_ words: [String], this color: UIColor) -> NSMutableAttributedString { let attributedString = NSMutableAttributedString(string: self) for word in words { let ranges = getRanges(of: word) for range in ranges { attributedString.addAttributes([NSForegroundColorAttributeName: color], range: range) } } return attributedString } } 

用法:

 // The strings you're interested in let string = "The dog ran after the cat" let words = ["the", "ran"] // Highlight words and get back attributed string let attributedString = string.highlight(words, this: .yellow) // Set attributed string label.attributedText = attributedString 

斯威夫特4

我想,有两种方法。

1. NSRange(range,in:)

2. NSRange(位置:,长度:)

示例代码:

 let attributedString = NSMutableAttributedString(string: "Sample Text 12345", attributes: [.font : UIFont.systemFont(ofSize: 15.0)]) // NSRange(range, in: ) if let range = attributedString.string.range(of: "Sample") { attributedString.addAttribute(.foregroundColor, value: UIColor.orange, range: NSRange(range, in: attributedString.string)) } // NSRange(location: , length: ) if let range = attributedString.string.range(of: "12345") { attributedString.addAttribute(.foregroundColor, value: UIColor.green, range: NSRange(location: range.lowerBound.encodedOffset, length: range.upperBound.encodedOffset - range.lowerBound.encodedOffset)) } 

屏幕截图: 在这里输入图像描述

 let text:String = "Hello Friend" let searchRange:NSRange = NSRange(location:0,length: text.characters.count) let range:Range`<Int`> = Range`<Int`>.init(start: searchRange.location, end: searchRange.length)