web-dev-qa-db-ja.com

NSAttributedStringのテキスト文字列全体を、他の属性を変更せずに置き換えます

NSAttributedStringへの参照があり、属性付き文字列のテキストを変更したい。

新しいNSAttributedStringを作成し、この新しい文字列で参照を更新する必要があると思います。しかし、これを行うと、以前の文字列の属性が失われます。

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

self.attributedTextの古い属性文字列への参照があります。新しい文字列で以前の属性を保持するにはどうすればよいですか?

29
Chirag Jain

NSMutableAttributedStringを使用して文字列を更新するだけで、属性は変更されません。例:

NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:@"my string" attributes:@{NSForegroundColorAttributeName: [UIColor blueColor], NSFontAttributeName: [UIFont systemFontOfSize:20]}];

//update the string
[mutableAttributedString.mutableString setString:@"my new string"];
35
Artal

Swift

属性を保持しながらテキストを変更します。

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)

mutableAttributedStringの結果は

  • enter image description here
  • enter image description here

インデックス0を超える属性のサブ範囲はすべて破棄されます。たとえば、元の文字列の最後の単語に別の属性を追加した場合、文字列を変更すると失われます。

// 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)

結果:

  • enter image description here
  • enter image description here

これから、新しい文字列は、元の文字列のインデックス0にある属性が何であっても取得できることがわかります。実際、範囲を調整して

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

我々が得る

  • enter image description here
  • enter image description here

こちらもご覧ください

31
Suragch

これを本当に簡単にするために少し拡張しました。

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

5
Wes

これはObjective-Cを使用する方法です(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];

最も重要な参照: attributesAtIndex:effectiveRange: および setAttributedString: を確認してください。

2

ダリウスの答えはほとんどそこにあります。軽微なエラーが含まれています。正しいのは:

これはObjective-Cを使用する方法です(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
        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;
        }

このコードがお役に立てば幸いです。ラベルの属性をmutableAttributedStringにコピーし、その文字列を設定しました

1
Syed Zahid Shah

変更可能な文字列のテキストを変更しても、最初の文字の属性のみが保持され、これがすべてのテキストに適用されるため、ジョブは実行されません。これは、ドキュメントの一部であるため、設計によるもののようです。

したがって、すべての属性をコピーするか、文字列を変更する場合は、すべての属性を手動でコピーする必要があります。次に、MutableAttributedStringを作成して、テキストを変更できます。その後、すべての属性を新しいMutableAttributedStringに適用します。

Xamarin(C#)の場合はこの方法で行いましたが、簡単に理解して言語に適合させることができると思います。

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

UIButtonを使用して作業している人のために、 Wes's に基づく改善された回答を以下に示します。

ボタンのラベルを更新するには、次のようにした方が良いように見えました:

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

だから私はこの拡張機能で終わった:

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

答えはどれも私にとってはうまくいきませんでしたが、これは答えです。

extension UILabel{
func setTextWhileKeepingAttributes(_ string: String) {
        if let attributedText = self.attributedText {
            let attributedString = NSMutableAttributedString(string: string,
                                                             attributes: [NSAttributedString.Key.font: font])
            attributedText.enumerateAttribute(.font, in: NSRange(location: 0, length: attributedText.length)) { (value, range, stop) in
                let attributes = attributedText.attributes(at: range.location, effectiveRange: nil)
                attributedString.addAttributes(attributes, range: range)
            }
            self.attributedText = attributedString
        }
    }
}
0
seymatanoglu