问题:当我使用使用Range的Swift String时,NSAttributedString接受NSRange
let text = "Long paragraph saying something goes here!" let textRange = text.startIndex..() in if (substring == "saying") { attributedString.addAttribute(NSForegroundColorAttributeName, value: NSColor.redColor(), range: substringRange) } })
产生以下错误:
错误:'Range'不能转换为'NSRange'aligolsString.addAttribute(NSForegroundColorAttributeName,value:NSColor.redColor(),range:substringRange)
Martin R.. 241
Swift String
范围和NSString
范围不是"兼容的".例如,表情符号就像一个Swift字符,但是作为两个NSString
字符(所谓的UTF-16代理对).
因此,如果字符串包含此类字符,则建议的解决方案将产生意外结果 例:
let text = "Long paragraph saying!" let textRange = text.startIndex..() 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!{ }
如你所见,"ph say"已经标有属性,而不是"说".
由于NS(Mutable)AttributedString
最终需要a NSString
和a 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的更新:
从Swift 4(Xcode 9)开始,Swift标准库提供了在Range
和之间进行转换的方法NSRange
.NSString
不再需要转换为:
let text = "Long paragraph saying!" let attributedString = NSMutableAttributedString(string: text) text.enumerateSubstrings(in: text.startIndex..这
substringRange
是一个Range
,并将其转换为相应NSRange
的NSRange(substringRange, in: text)
对于想要在OSX上键入表情符号字符的任何人 - 控制 - 命令空间栏会显示一个字符选择器 (70认同)
如果我匹配多个单词,这不起作用,我不确定要匹配的整个字符串是什么.假设我从API返回一个字符串并在另一个字符串中使用它,我希望API中的字符串加下划线,我不能保证子字符串不会出现在API的字符串和其他字符串中串!有任何想法吗? (2认同)
你提到`Range
`和`NSString`是不兼容的.他们的同行也不相容吗?即`NSRange`和`String`不兼容?因为Apple的API之一特别结合了两者:[matches(in:options:range:)](https://developer.apple.com/reference/foundation/nsregularexpression/1412446-matches) (2认同)
royherma.. 53
对于像你描述的那样的情况,我发现这是有用的.它相对简短而且甜美:
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)
attributionString.addAttribute不适用于swift Range (9认同)
@Paludis,你是对的,但这个解决方案并没有尝试使用Swift系列.它使用的是"NSRange".`str`是一个`NSString`,因此`str.RangeOfString()`返回一个`NSRange`. (6认同)
你也可以删除第2行中的重复字符串,方法是将第2行和第3行替换为:`let str = attributionString.string as NSString` (3认同)
George Maisu.. 23
答案很好,但使用Swift 4可以简化代码:
let text = "Test string" let substring = "string" let substringRange = text.range(of: substring)! let nsRange = NSRange(substringRange, in: text)请注意,因为
range
功能的结果必须打开.
可能解决方案
Swift提供distance(),用于测量可用于创建NSRange的start和end之间的距离:
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) } })
斯威夫特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
我知道在大多数情况下这个初始化就足够了.看它的用法:
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,如果您没有目标字符串,则可以直接创建转换
extension NSRange { public init(_ range:Range<String.Index>) { self.init(location: range.lowerBound.encodedOffset, length: range.upperBound.encodedOffset - range.lowerBound.encodedOffset) } }
或者您可以为Range本身创建专门的扩展
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)) // "" }
对我来说,这很完美:
let font = UIFont.systemFont(ofSize: 12, weight: .medium) let text = "text" let attString = NSMutableAttributedString(string: "exemple text :)") attString.addAttributes([.font: font], range:(attString.string as NSString).range(of: text)) label.attributedText = attString
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!{ }
如你所见,"ph say"已经标有属性,而不是"说".
由于NS(Mutable)AttributedString
最终需要a NSString
和a 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的更新:
从Swift 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)
答案很好,但使用Swift 4可以简化代码:
let text = "Test string" let substring = "string" let substringRange = text.range(of: substring)! let nsRange = NSRange(substringRange, in: text)
请注意,因为range
功能的结果必须打开.