ios objective-c nsattributedstring

ios - Reemplace la cadena de texto completa en NSAttributedString sin modificar otros atributos



objective-c (8)

Tengo una referencia a NSAttributedString y quiero cambiar el texto de la cadena atribuida.

Supongo que tengo que crear una nueva NSAttributedString y actualizar la referencia con esta nueva cadena. Sin embargo, cuando hago esto pierdo el atributo de la cadena anterior.

NSAttributedString *newString = [[NSAttributedString alloc] initWithString:text]; [self setAttributedText:newString];

Tengo referencia a la vieja cadena atribuida en self.attributedText . ¿Cómo puedo conservar el atributo anterior en la nueva cadena?


Cambiar el texto de una cadena mutable no hará los trabajos, ya que solo mantendrá los atributos del primer carácter y lo aplicará a todo el texto. Lo que parece ser por diseño, ya que forma parte de la documentación.

Entonces, si desea copiar todos los atributos o cambiar la cadena, necesita copiar todos los atributos manualmente. Luego puedes crear una MutableAttributedString y cambiar el texto. Después, aplica todos los atributos a la nueva MutableAttributedString.

Lo he hecho de esta manera para Xamarin (en C #), pero creo que puedes entenderlo fácilmente y adaptarlo a tu idioma:

NSMutableAttributedString result = new NSMutableAttributedString(attrStr.Value.Replace(blackSquare, bullet)); // You cannot simply replace an AttributedString''s string, because that will discard attributes. // Therefore, I will now copy all attributes manually to the new MutableAttributedString: NSRange outRange = new NSRange(0, 0); int attributeIndex = 0; while (outRange.Location + outRange.Length < attrStr.Value.Length // last attribute range reached && attributeIndex < attrStr.Value.Length) // or last character reached { // Get all attributes for character at attributeIndex var attributes = attrStr.GetAttributes(attributeIndex, out outRange); if (attributes != null && attributes.Count > 0) { result.AddAttributes(attributes, outRange); // copy all found attributes to result attributeIndex = (int)(outRange.Location + outRange.Length); // continue with the next range } else { attributeIndex++; // no attribues at the current attributeIndex, so continue with the next char } } // all attributes are copied


Esta es la forma de usar Objective-C (probado en iOS 9)

NSAttributedString *primaryString = ...; NSString *newString = ...; //copy the attributes NSDictionary *attributes = [primaryString attributesAtIndex:0 effectiveRange:NSMakeRange(primaryString.length-1, primaryString.length)]; NSMutableAttributedString *newString = [[NSMutableAttributedString alloc] initWithString:newString attributes:attributes]; NSMutableAttributedString *primaryStringMutable = [[NSMutableAttributedString alloc] initWithAttributedString:primaryString]; //change the string [primaryStringMutable setAttributedString::newString]; primaryString = [NSAttributedString alloc] initWithAttributedString:primaryStringMutable];

Verifique las referencias más importantes: attributesAtIndex:effectiveRange: setAttributedString: y setAttributedString:


Hice una pequeña extensión para hacer esto realmente fácil:

import UIKit extension UILabel { func setTextWhileKeepingAttributes(string: String) { if let newAttributedText = self.attributedText { let mutableAttributedText = newAttributedText.mutableCopy() mutableAttributedText.mutableString.setString(string) self.attributedText = mutableAttributedText as? NSAttributedString } } }

https://gist.github.com/wvdk/e8992e82b04e626a862dbb991e4cbe9c


La respuesta de Darío está casi ahí. Contiene un error menor. El correcto es:

Esta es la forma de usar Objective-C (probado en iOS 10)

NSAttributedString *primaryString = ...; NSString *newString = ...; //copy the attributes NSRange range = NSMakeRange(primaryString.length-1, primaryString.length); NSDictionary *attributes = [primaryString attributesAtIndex:0 effectiveRange:&range]; NSMutableAttributedString *newString = [[NSMutableAttributedString alloc] initWithString:newString attributes:attributes]; NSMutableAttributedString *primaryStringMutable = [[NSMutableAttributedString alloc] initWithAttributedString:primaryString]; //change the string [primaryStringMutable setAttributedString::newString]; primaryString = [NSAttributedString alloc] initWithAttributedString:primaryStringMutable];


Para aquellos de ustedes que trabajan con UIButtons, aquí hay una respuesta mejorada basada en la de Wes''s .

Parecía que la actualización de una etiqueta de un botón debería hacerse de esta manera:

let newtext = "my new text" myuibutton.setAttributedTitle(titlelabel.getTextWhileKeepingAttributes(string: newtext), for: .normal)

Así que terminé con esta extensión:

import UIKit extension UILabel { func setTextWhileKeepingAttributes(string: String) { if let newAttributedText = self.attributedText { let mutableAttributedText = newAttributedText.mutableCopy() (mutableAttributedText as AnyObject).mutableString.setString(string) self.attributedText = mutableAttributedText as? NSAttributedString } } func getTextWhileKeepingAttributes(string: String) -> NSAttributedString { if let newAttributedText:NSAttributedString = self.attributedText { let mutableAttributedText = newAttributedText.mutableCopy() (mutableAttributedText as AnyObject).mutableString.setString(string) return mutableAttributedText as! NSAttributedString } else { // No attributes in this label, just create a new attributed string? let attributedstring = NSAttributedString.init(string: string) return attributedstring } } }


Puede usar NSMutableAttributedString y solo actualizar la cadena, los atributos no cambiarán. Ejemplo:

NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:@"my string" attributes:@{NSForegroundColorAttributeName: [UIColor blueColor], NSFontAttributeName: [UIFont systemFontOfSize:20]}]; //update the string [mutableAttributedString.mutableString setString:@"my new string"];


Versión rápida

Cambia el texto manteniendo los atributos:

let myString = "my string" let myAttributes = [NSForegroundColorAttributeName: UIColor.blue, NSFontAttributeName: UIFont.systemFont(ofSize: 40)] let mutableAttributedString = NSMutableAttributedString(string: myString, attributes: myAttributes) let myNewString = "my new string" mutableAttributedString.mutableString.setString(myNewString)

Los resultados para mutableAttributedString son

Notas

Cualquier sub-rango de atributos más allá del índice 0 se descarta. Por ejemplo, si agrego otro atributo a la última palabra de la cadena original, se pierde después de cambiar la cadena:

// additional attribute added before changing the text let myRange = NSRange(location: 3, length: 6) let anotherAttribute = [ NSBackgroundColorAttributeName: UIColor.yellow ] mutableAttributedString.addAttributes(anotherAttribute, range: myRange)

Resultados:

De esto podemos ver que la nueva cadena obtiene los atributos que están en el índice 0 de la cadena original. De hecho, si ajustamos el rango para que sea

let myRange = NSRange(location: 0, length: 1)

obtenemos

Ver también

  • Mi respuesta principal sobre las cuerdas Swift atribuidas

let mutableAttributedString = mySubTitleLabel.attributedText?.mutableCopy() as? NSMutableAttributedString if let attrStr = mutableAttributedString{ attrStr.mutableString.setString("Inner space can be an example shown on the, third page of the tutorial.") mySubTitleLabel.attributedText = attrStr; }

Espero que este código te pueda ayudar, he copiado el atributo de la etiqueta a mutableAttributedString y luego le puse la cadena.