J'ai une référence à NSAttributedString
et je veux changer le texte de la chaîne attribuée.
Je suppose que je dois créer un nouveau NSAttributedString
et mettre à jour la référence avec cette nouvelle chaîne. Cependant, lorsque je fais cela, je perds l'attribut de la chaîne précédente.
NSAttributedString *newString = [[NSAttributedString alloc] initWithString:text];
[self setAttributedText:newString];
J'ai référence à l'ancienne chaîne attribuée dans self.attributedText
. Comment puis-je conserver l'attribut précédent dans la nouvelle chaîne?
Vous pouvez utiliser NSMutableAttributedString et simplement mettre à jour la chaîne, les attributs ne changeront pas . Exemple:
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:@"my string" attributes:@{NSForegroundColorAttributeName: [UIColor blueColor], NSFontAttributeName: [UIFont systemFontOfSize:20]}];
//update the string
[mutableAttributedString.mutableString setString:@"my new string"];
Changer le texte en gardant les attributs:
let myString = "my string"
let myAttributes = [NSAttributedString.Key.foregroundColor: UIColor.blue, NSAttributedString.Key.font: UIFont.systemFont(ofSize: 40)]
let mutableAttributedString = NSMutableAttributedString(string: myString, attributes: myAttributes)
let myNewString = "my new string"
mutableAttributedString.mutableString.setString(myNewString)
Les résultats pour mutableAttributedString
sont
Remarques
Toute sous-plage d'attributs au-delà de l'index 0 est ignorée. Par exemple, si j'ajoute un autre attribut au dernier mot de la chaîne d'origine, il est perdu après avoir modifié la chaîne:
// additional attribute added before changing the text
let myRange = NSRange(location: 3, length: 6)
let anotherAttribute = [ NSAttributedString.Key.backgroundColor: UIColor.yellow ]
mutableAttributedString.addAttributes(anotherAttribute, range: myRange)
Résultats:
De là, on peut voir que la nouvelle chaîne obtient quels que soient les attributs qui sont à l'index 0 de la chaîne d'origine. En effet, si on ajuste la plage pour être
let myRange = NSRange(location: 0, length: 1)
on a
Voir également
J'ai fait une petite extension pour rendre cela vraiment facile:
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
C’est ce qui se passe avec Objective-C (testé sur 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];
Recherchez les références les plus importantes: attributesAtIndex: effectiveRange: et setAttributedString: .
La réponse de Darius est presque là. Il contient une erreur mineure. Le correct est:
C’est ce qui se passe avec Objective-C (testé sur 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];
Changer le texte d'une chaîne mutable ne fera pas le travail, car il ne conservera que les attributs du premier caractère et l'appliquera à tout le texte. Ce qui semble être inhérent à la conception, car cela fait partie de la documentation.
Ainsi, si vous souhaitez copier tous les attributs ou modifier la chaîne, vous devez copier tous les attributs manuellement. Ensuite, vous pouvez créer un MutableAttributedString et changer le texte. Ensuite, vous appliquez tous les attributs au nouveau MutableAttributedString.
Je l’ai fait de cette façon pour Xamarin (en C #), mais je pense que vous pouvez facilement le comprendre et l’adapter à votre langage:
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
Pour ceux d'entre vous qui travaillent avec UIButtons, voici une réponse améliorée basée sur Wes's .
Il semblait que la mise à jour de l’étiquette d’un bouton aurait dû être faite de la façon suivante:
let newtext = "my new text"
myuibutton.setAttributedTitle(titlelabel.getTextWhileKeepingAttributes(string: newtext), for: .normal)
Alors j'ai fini avec cette extension:
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
}
}
}
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;
}
J'espère que ce code peut vous aider, j'ai copié l'attribut de l'étiquette sur un mutableAttributedString puis défini la chaîne correspondante.