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.