来自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)
请注意, range
function的结果必须解开。
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)