Reemplace la subcadena de NSAttributedString con otra NSAttributedString

Quiero reemplazar una subcadena (por ejemplo, @"replace" ) de un NSAttributedString con otro NSAttributedString .

Estoy buscando un método equivalente al NSString de stringByReplacingOccurrencesOfString:withString: para NSAttributedString .

  1. Convierta su cadena atribuida en una instancia de NSMutableAttributedString .

  2. La cadena atribuible mutable tiene una propiedad mutableString . De acuerdo con la documentación:

    “El receptor rastrea los cambios en esta cadena y mantiene sus asignaciones de atributos actualizadas”.

    De modo que puede usar la cadena mutable resultante para ejecutar el reemplazo con replaceOccurrencesOfString:withString:options:range:

Aquí es cómo puede cambiar la cadena de NSMutableAttributedString, conservando sus atributos:

Rápido:

 // first we create a mutable copy of attributed text let originalAttributedText = nameLabel.attributedText?.mutableCopy() as! NSMutableAttributedString // then we replace text so easily let newAttributedText = originalAttributedText.mutableString.setString("new text to replace") 

C objective:

 NSMutableAttributedString *newAttrStr = [attribtedTxt.mutableString setString:@"new string"]; 

En mi caso, la siguiente manera fue la única (probado en iOS9):

 NSAttributedString *attributedString = ...; NSAttributedString *anotherAttributedString = ...; //the string which will replace while ([attributedString.mutableString containsString:@"replace"]) { NSRange range = [attributedString.mutableString rangeOfString:@"replace"]; [attributedString replaceCharactersInRange:range withAttributedString:anotherAttributedString]; } 

Por supuesto, sería bueno encontrar otra forma mejor.

Con Swift 4 y iOS 11, puede usar una de las 2 formas siguientes para resolver su problema.


# 1. Usando NSMutableAttributedString replaceCharacters(in:with:) método

NSMutableAttributedString tiene un método llamado replaceCharacters(in:with:) . replaceCharacters(in:with:) tiene la siguiente statement:

Reemplaza los caracteres y atributos en un rango dado con los caracteres y atributos de la cadena atribuida dada.

 func replaceCharacters(in range: NSRange, with attrString: NSAttributedString) 

El código de Playground a continuación muestra cómo usar replaceCharacters(in:with:) con el fin de reemplazar una subcadena de una instancia de NSMutableAttributedString con una nueva instancia de NSMutableAttributedString :

 import UIKit // Set initial attributed string let initialString = "This is the initial string" let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red] let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes) // Set new attributed string let newString = "new" let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue] let newAttributedString = NSMutableAttributedString(string: newString, attributes: newAttributes) // Get range of text to replace guard let range = mutableAttributedString.string.range(of: "initial") else { exit(0) } let nsRange = NSRange(range, in: mutableAttributedString.string) // Replace content in range with the new content mutableAttributedString.replaceCharacters(in: nsRange, with: newAttributedString) 

# 2. Usando el NSMutableString replaceOccurrences(of:with:options:range:)

NSMutableString tiene un método llamado replaceOccurrences(of:with:options:range:) . replaceOccurrences(of:with:options:range:) tiene la siguiente statement:

Reemplaza todas las ocurrencias de una cadena dada en un rango dado con otra cadena dada, devolviendo el número de reemplazos.

 func replaceOccurrences(of target: String, with replacement: String, options: NSString.CompareOptions = [], range searchRange: NSRange) -> Int 

El código de Playground a continuación muestra cómo usar replaceOccurrences(of:with:options:range:) para reemplazar una subcadena de una instancia de NSMutableAttributedString con una nueva instancia de NSMutableAttributedString :

 import UIKit // Set initial attributed string let initialString = "This is the initial string" let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red] let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes) // Set new string let newString = "new" // Replace replaceable content in mutableAttributedString with new content let totalRange = NSRange(location: 0, length: mutableAttributedString.string.count) _ = mutableAttributedString.mutableString.replaceOccurrences(of: "initial", with: newString, options: [], range: totalRange) // Get range of text that requires new attributes guard let range = mutableAttributedString.string.range(of: newString) else { exit(0) } let nsRange = NSRange(range, in: mutableAttributedString.string) // Apply new attributes to the text matching the range let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue] mutableAttributedString.setAttributes(newAttributes, range: nsRange) 

Tuve que escribir texto en negrita en las tags , aquí lo que hice:

 - (NSAttributedString *)boldString:(NSString *)string { UIFont *boldFont = [UIFont boldSystemFontOfSize:14]; NSMutableAttributedString *attributedDescription = [[NSMutableAttributedString alloc] initWithString:string]; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@".*?(.*?)< \\/b>.*?" options:NSRegularExpressionCaseInsensitive error:NULL]; NSArray *myArray = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)] ; for (NSTextCheckingResult *match in myArray) { NSRange matchRange = [match rangeAtIndex:1]; [attributedDescription addAttribute:NSFontAttributeName value:boldFont range:matchRange]; } while ([attributedDescription.string containsString:@""] || [attributedDescription.string containsString:@""]) { NSRange rangeOfTag = [attributedDescription.string rangeOfString:@""]; [attributedDescription replaceCharactersInRange:rangeOfTag withString:@""]; rangeOfTag = [attributedDescription.string rangeOfString:@""]; [attributedDescription replaceCharactersInRange:rangeOfTag withString:@""]; } return attributedDescription; } 
 NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithString:@"I am a boy."]; [result addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, [result length])]; NSMutableAttributedString *replace = [[NSMutableAttributedString alloc] initWithString:@"a"]; [replace addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [replace length])]; [result replaceCharactersInRange:NSMakeRange(5, [replace length]) withAttributedString:replace]; 

Encuentro que todas las demás respuestas no funcionan. Aquí es cómo reemplacé el contenido de una cadena NSAttributed en una extensión de categoría:

 func stringWithString(stringToReplace:String, replacedWithString newStringPart:String) -> NSMutableAttributedString { let mutableAttributedString = mutableCopy() as! NSMutableAttributedString let mutableString = mutableAttributedString.mutableString while mutableString.containsString(stringToReplace) { let rangeOfStringToBeReplaced = mutableString.rangeOfString(stringToReplace) mutableAttributedString.replaceCharactersInRange(rangeOfStringToBeReplaced, withString: newStringPart) } return mutableAttributedString } 

Tengo un requisito específico y lo fijo como a continuación. Esto podría ayudar a alguien.

Requisito: en el guión gráfico, el texto enriquecido se agrega directamente al atributo UITextView que contiene una palabra “Versión de la aplicación: 1.0”. Ahora tengo que dinamizar el número de versión leyéndolo de info plist.

Solución: Se eliminó la versión número 1.0 del guión gráfico, solo se guardó la “Versión de la aplicación:” y se agregó el código siguiente.

 NSAttributedString *attribute = self.firsttextView.attributedText; NSMutableAttributedString *mutableAttri = [[NSMutableAttributedString alloc] initWithAttributedString:attribute]; NSString *appVersionText = @"App Version:"; if ([[mutableAttri mutableString] containsString:appVersionText]) { NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary]; NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"]; NSString *newappversion = [NSString stringWithFormat:@"%@ %@",appVersionText,version] ; [[mutableAttri mutableString] replaceOccurrencesOfString:appVersionText withString:newappversion options:NSCaseInsensitiveSearch range:NSMakeRange(0, mutableAttri.length)]; self.firsttextView.attributedText = mutableAttri; } 

¡¡Hecho!! Actualizado / modificado attributeText.

Swift 4: la solución Sunkas excelente actualizada a Swift 4 y envuelta en “extensión”. Simplemente córtelo en su ViewController (fuera de la clase) y úselo.

 extension NSAttributedString { func stringWithString(stringToReplace: String, replacedWithString newStringPart: String) -> NSMutableAttributedString { let mutableAttributedString = mutableCopy() as! NSMutableAttributedString let mutableString = mutableAttributedString.mutableString while mutableString.contains(stringToReplace) { let rangeOfStringToBeReplaced = mutableString.range(of: stringToReplace) mutableAttributedString.replaceCharacters(in: rangeOfStringToBeReplaced, with: newStringPart) } return mutableAttributedString } }