Colorea todas las ocurrencias de cuerda en veloz

Este código

var textSearch="hi" var textToShow="hi hihi hi" var rangeToColor = (textToShow as NSString).rangeOfString(textSearch) var attributedString = NSMutableAttributedString(string:textToShow) attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellowColor() , range: rangeToColor) TextView.attributedText=attributedString 

me da NSRange para colorear una cadena dentro de TextView. El problema es que solo devuelvo la primera ocurrencia. Si la palabra contiene “hi hihi hi”, solo el primer “hola” está coloreado. ¿Cómo puedo obtener todas las apariciones de la cadena?

Swift 3

 let attrStr = NSMutableAttributedString(string: "hi hihi hey") let inputLength = attrStr.string.characters.count let searchString = "hi" let searchLength = searchString.characters.count var range = NSRange(location: 0, length: attrStr.length) while (range.location != NSNotFound) { range = (attrStr.string as NSString).range(of: searchString, options: [], range: range) if (range.location != NSNotFound) { attrStr.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellow(), range: NSRange(location: range.location, length: searchLength)) range = NSRange(location: range.location + range.length, length: inputLength - (range.location + range.length)) } } 

Swift 2

 let attrStr = NSMutableAttributedString(string: "hi hihi hey") let inputLength = attrStr.string.characters.count let searchString = "hi" let searchLength = searchString.characters.count var range = NSRange(location: 0, length: attrStr.length) while (range.location != NSNotFound) { range = (attrStr.string as NSString).rangeOfString(searchString, options: [], range: range) if (range.location != NSNotFound) { attrStr.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellowColor(), range: NSRange(location: range.location, length: searchLength)) range = NSRange(location: range.location + range.length, length: inputLength - (range.location + range.length)) } } 

Azúcar de syntax para la respuesta de Kevin anterior.

Llamado como:

 attrStr.attributeRangeFor(searchString, attributeValue: UIColor.yellowColor(), atributeSearchType: .All) 

Swift 2.0:

 import UIKit extension NSMutableAttributedString { enum AtributeSearchType { case First, All, Last } func attributeRangeFor(searchString: String, attributeValue: AnyObject, atributeSearchType: AtributeSearchType) { let inputLength = self.string.characters.count let searchLength = searchString.characters.count var range = NSRange(location: 0, length: self.length) var rangeCollection = [NSRange]() while (range.location != NSNotFound) { range = (self.string as NSString).rangeOfString(searchString, options: [], range: range) if (range.location != NSNotFound) { switch atributeSearchType { case .First: self.addAttribute(NSForegroundColorAttributeName, value: attributeValue, range: NSRange(location: range.location, length: searchLength)) return case .All: self.addAttribute(NSForegroundColorAttributeName, value: attributeValue, range: NSRange(location: range.location, length: searchLength)) break case .Last: rangeCollection.append(range) break } range = NSRange(location: range.location + range.length, length: inputLength - (range.location + range.length)) } } switch atributeSearchType { case .Last: let indexOfLast = rangeCollection.count - 1 self.addAttribute(NSForegroundColorAttributeName, value: attributeValue, range: rangeCollection[indexOfLast]) break default: break } } } 

Swift 4:

 let string = "foo fbar foofoo foofo" let mutableAttributedString = NSMutableAttributedString(string: string) let searchString = "foo" var rangeToSearch = string.startIndex.. 

El uso de NSRegularExpression evita hacer todos los cálculos de rango usted mismo. Este ejemplo también resaltará dos palabras en lugar de solo una.

 let text = "If you don't have a plan, you become part of somebody else's plan." let toHighlight = ["plan", "you"] let range = text.nsRange(from: text.startIndex ..< text.endIndex) // full text let rangesToHighlight: [[NSRange]] = toHighlight.map { search in do { let regex = try NSRegularExpression(pattern: search, options: []) let matches: [NSTextCheckingResult] = regex.matches(in: text, options: [], range: range) return matches.map { $0.range } // get range from NSTextCheckingResult } catch { return [NSRange]() } } let yellow = UIColor.yellow let attributedText = NSMutableAttributedString(string: text) let flattenedRanges: [NSRange] = rangesToHighlight.joined() flattenedRanges.forEach { // apply color to all ranges attributedText.addAttribute(NSForegroundColorAttributeName, value: yellow, range: $0) } 

He creado una extensión para él en veloz 4.2

 extension NSMutableAttributedString { // Adds attributes EVERY TIME the text to change appears func addAttributes(_ attributes: [NSAttributedString.Key: NSObject], forText text: String) { var range = NSRange(location: 0, length: self.length) while (range.location != NSNotFound) { range = (self.string as NSString).range(of: text, options: [], range: range) if (range.location != NSNotFound) { self.addAttributes(attributes, range: NSRange(location: range.location, length: text.count)) range = NSRange(location: range.location + range.length, length: self.string.count - (range.location + range.length)) } } } 

Ahora puedes llamarlo así:

 let attributedString = NSMutableAttributedString(attributedString: textView.attributedText) let myAttributes = [/* your attributes here */] attributedString.addAttributes(myAttributes, forText: /* your text here */) 

Hice esos dos métodos para colorear solo una vez o colorear todo el suceso para ese texto:

 extension NSMutableAttributedString{ func setColorForText(_ textToFind: String, with color: UIColor) { let range = self.mutableString.range(of: textToFind, options: .caseInsensitive) if range.location != NSNotFound { addAttribute(NSForegroundColorAttributeName, value: color, range: range) } } func setColorForAllOccuranceOfText(_ textToFind: String, with color: UIColor) { let inputLength = self.string.count let searchLength = textToFind.count var range = NSRange(location: 0, length: self.length) while (range.location != NSNotFound) { range = (self.string as NSString).range(of: textToFind, options: [], range: range) if (range.location != NSNotFound) { self.addAttribute(NSForegroundColorAttributeName, value: color, range: NSRange(location: range.location, length: searchLength)) range = NSRange(location: range.location + range.length, length: inputLength - (range.location + range.length)) } } } }