web-dev-qa-db-fra.com

remplacer la chaîne de texte entière dans NSAttributedString sans modifier d'autres attributs

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?

26
Chirag Jain

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"];
32
Artal

Rapide

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

27
Suragch

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

4
Wes

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: .

2
Darius Miliauskas

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];
1
SpaceDog

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
0
Viktor

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
        }
    }
}
0
Jonny
        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.

0
Syed Zahid Shah