完美的排球 · 1998年印尼排华真实影像,无数华人女性被当街侮辱· 5 月前 · |
坏坏的煎饼果子 · 武汉纺织大学外经贸学院是几本-是二本还是三本 ...· 1 年前 · |
神勇威武的炒饭 · 异域档案之暹罗密码的个人页· 1 年前 · |
博学的警车 · 12530中国移动彩铃- 知乎· 1 年前 · |
忧郁的日光灯 · 文在寅卸任前会被送进监狱吗?能打破韩国总统必 ...· 1 年前 · |
问题: NSAttributedString接受NSRange,而我正在使用使用范围的Swift字符串
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)
})
会产生以下错误:
范围错误:‘attributedString.addAttribute(NSForegroundColorAttributeName,’不能转换为'NSRange‘值: NSColor.redColor(),range: substringRange)
可能的解决方案
Swift提供了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)
})
对于你描述的这种情况,我发现这是可行的。它相对简短而甜蜜:
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)
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
}
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)
我喜欢Swift语言,但是使用带有与
NSRange
不兼容的Swift
Range
的
NSAttributedString
让我头疼了很长时间。因此,为了绕过所有这些垃圾,我设计了以下方法来返回一个
NSMutableAttributedString
,其中突出显示的单词设置为您的颜色。
对于表情符号,这不适用于。如有必要,请修改。
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
答案很好,但是使用Swift 4你可以简化你的代码:
let text = "Test string"
let substring = "string"
let substringRange = text.range(of: substring)!
let nsRange = NSRange(substringRange, in: text)
要小心,因为
range
函数的结果必须被解包。
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实例。
而不是一般的初始化用法,它要求您将目标参数作为字符串,如果您手头没有目标字符串,可以直接创建转换
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)) // ""
}
Swift 5:
由于默认情况下Swift字符串迁移到UTF-8编码,因此
encodedOffset
的使用被认为是不推荐使用的,如果没有字符串本身的实例,就不能将范围转换为NSRange,因为为了计算偏移量,我们需要以UTF-8编码的源字符串,并且应该在计算偏移量之前将其转换为UTF-16。因此,目前最好的方法是使用泛型init。
Swift 4
我认为,有两种方法。
范围1.NSRange( ,in:)
2. NSRange(location:,length:)
示例代码:
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))
}
屏幕截图:
保留现有属性的 Swift 3扩展变体 。
extension UILabel {
func setLineHeight(lineHeight: CGFloat) {
guard self.text != nil && self.attributedText != nil else { return }
var attributedString = NSMutableAttributedString()
if let attributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: attributedText)
} else if let text = self.text {
attributedString = NSMutableAttributedString(string: text)
let style = NSMutableParagraphStyle()
style.lineSpacing = lineHeight
style.alignment = self.textAlignment
let str = NSString(string: attributedString.string)
attributedString.addAttribute(NSParagraphStyleAttributeName,
value: style,
range: str.range(of: str as String))
self.attributedText = attributedString
}
对我来说,这很好用:
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
我的解决方案是一个字符串扩展,它首先获取快速范围,然后获取从字符串的开始到子字符串的开始和结束的距离。
然后使用这些值计算子字符串的起始和长度。然后,我们可以将这些值应用于NSMakeRange构造函数。
这个解决方案使用由多个单词组成的子字符串,这里使用enumerateSubstrings的许多解决方案都让我失望。
extension String {
func NSRange(of substring: String) -> NSRange? {
// Get the swift range
guard let range = range(of: substring) else { return nil }
// Get the distance to the start of the substring
let start = distance(from: startIndex, to: range.lowerBound) as Int
//Get the distance to the end of the substring
let end = distance(from: startIndex, to: range.upperBound) as Int
//length = endOfSubstring - startOfSubstring
//start = startOfSubstring
return NSMakeRange(start, end - start)
}
Swift 5解决方案
将Range转换为NSRange
Int作为 'encodedOffset' 已被弃用,因此现在为了将 String.Index 转换为 Int ,我们需要引用从中派生 Range 的原始字符串。
一个方便的针对NSRange的 详细 扩展可能如下所示:
extension NSRange {
public init(range: Range<String.Index>,
originalText: String) {
let range_LowerBound_INDEX = range.lowerBound
let range_UpperBound_INDEX = range.upperBound
let range_LowerBound_INT = range_LowerBound_INDEX.utf16Offset(in: originalText)
let range_UpperBound_INT = range_UpperBound_INDEX.utf16Offset(in: originalText)
let locationTemp = range_LowerBound_INT
let lengthTemp = range_UpperBound_INT - range_LowerBound_INT
self.init(location: locationTemp,
length: lengthTemp)
}
而 速记 扩展如下所示
extension NSRange {
public init(range: Range<String.Index>,
originalText: String) {
self.init(location: range.lowerBound.utf16Offset(in: originalText),
length: range.upperBound.utf16Offset(in: originalText) - range.lowerBound.utf16Offset(in: originalText))
}
现在我们可以使用任何范围将其转换为NSRange,如下所示,分享了我自己的需求,这导致我编写了上面的扩展
我使用下面的字符串扩展从字符串中查找特定单词的所有范围
extension String {
func ranges(of substring: String, options: CompareOptions = [], locale: Locale? = nil) -> [Range<Index>] {
var ranges: [Range<Index>] = []
while let range = range(of: substring, options: options, range: (ranges.last?.upperBound ?? self.startIndex)..<self.endIndex, locale: locale) {
ranges.append(range)
return ranges
}
我的需求是更改字符串中特定单词的颜色,因此我编写了这个扩展来完成这项工作
extension NSAttributedString {
static func colored(originalText:String,
wordToColor:String,
currentColor:UIColor,
differentColor:UIColor) -> NSAttributedString {
let attr = NSMutableAttributedString(string: originalText)
attr.beginEditing()
attr.addAttribute(NSAttributedString.Key.foregroundColor,
value: currentColor,
range: NSRange(location: 0, length: originalText.count))
// FOR COVERING ALL THE OCCURENCES
for eachRange in originalText.ranges(of: wordToColor) {
attr.addAttribute(NSAttributedString.Key.foregroundColor,
value: differentColor,
range: NSRange(range: eachRange, originalText: originalText))
attr.endEditing()
return attr
完美的排球 · 1998年印尼排华真实影像,无数华人女性被当街侮辱 5 月前 |
神勇威武的炒饭 · 异域档案之暹罗密码的个人页 1 年前 |
博学的警车 · 12530中国移动彩铃- 知乎 1 年前 |