Заменить всю текстовую строку в NSAttributedString без изменения других атрибутов

У меня есть ссылка на NSAttributedString, и я хочу изменить текст атрибутной строки.

Думаю, мне нужно создать новый NSAttributedString и обновить ссылку этой новой строкой. Однако, когда я делаю это, я теряю атрибут предыдущей строки.

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

У меня есть ссылка на старую атрибутную строку в self.attributedText. Как я могу сохранить предыдущий атрибут в новой строке?

Ответ 1

Вы можете использовать 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"];

Ответ 2

стриж

Измените текст, сохранив атрибуты:

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

Смотрите также

Ответ 3

Это способ использования 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:.

Ответ 4

Я сделал небольшое расширение, чтобы сделать это очень просто:

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

Дариус отвечает почти. Он содержит незначительную ошибку. Правильно:

Это способ использования 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];

Ответ 6

Для тех из вас, кто работает с UIButtons, вот улучшенный ответ на основе Wes.

Казалось, что обновление ярлыка кнопки лучше было бы сделать следующим образом:

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
        }
    }
}

Ответ 7

Изменение текста изменяемой строки не выполнит заданий, поскольку оно сохранит только атрибуты первого символа и применимо ко всему тексту. Что, похоже, задумано, так как это часть документации.

Поэтому, если вы хотите скопировать все атрибуты или изменить строку, вам необходимо скопировать все атрибуты вручную. Затем вы можете создать MutableAttributedString и изменить текст. После этого вы применяете все атрибуты к новой MutableAttributedString.

Я сделал это таким образом для Xamarin (в С#), но я думаю, что вы можете легко понять это и адаптировать его для вашего языка:

NSMutableAttributedString result = new 
NSMutableAttributedString(attrStr.Value.Replace(blackSquare, bullet));
// You cannot simply replace an AttributedString 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

Ответ 8

        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 и затем установил строку для него