NSRange из диапазона Swift?
: NSAttributedString принимает NSRange, пока я использую строку Swift, которая использует Range
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 (), range: substringRange)
11 ответов:
Свифт
String
диапазоны иNSString
диапазоны не "совместимы". Например, эмодзи, как считается, как один быстрый символ, но как два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 "был отмечен атрибутом, а не"saying".
С тех пор
NS(Mutable)AttributedString
в конечном счете требуетсяNSString
и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)
для случаев, подобных описанному вами, я обнаружил, что это работает. Это относительно короткий и сладкий:
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)
Возможное Решение
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) } })
ответы в порядке, но с 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))) // "" }
но преобразование может быть сделано непосредственно из диапазона to NSRange без экземпляра строки Swift.
вместо обобщенного init использование, которое требует от вас цель параметр as строка и Если вы не надо цель строка под рукой вы можете создать преобразование непосредственно
extension NSRange { public init(_ range:Range<String.Index>) { self.init(location: range.lowerBound.encodedOffset, length: range.upperBound.encodedOffset - range.lowerBound.encodedOffset) } }
или вы можете создать специализированное расширение для диапазон себя
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 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 } }
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 }
Я люблю быстрый язык, но с помощью
NSAttributedString
со СвифтомRange
это не совместимо сNSRange
у меня слишком долго болела голова. Поэтому, чтобы обойти весь этот мусор, я разработал следующие методы для возврата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
Я думаю, есть два пути.
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)) }