How do you clear attributes from NSMutableAttributedString?
Asked Answered
G

6

14

How do you set all the attributes in an NSMutableAttributedString to nothing? Do you have to enumerate through them and remove them?

I don't want to create a new one. I am working on the textStorage in NSTextView. Setting a new string resets the cursor position in NSTextView and fires the delegate.

Guttural answered 5/3, 2015 at 14:57 Comment(2)
You could use [attributedString string] and recreate it with no attributes.Proteus
See edits above - specifically I dont want to create a new NSMutableAttributedString - sorry to everyone for lack of clarity in questionGuttural
D
22

You can remove all of the attributes like this:

NSMutableAttributedString *originalMutableAttributedString = //your string…

NSRange originalRange = NSMakeRange(0, originalMutableAttributedString.length);
[originalMutableAttributedString setAttributes:@{} range:originalRange];

Note that this uses setAttributes (not add). From the docs:

These new attributes replace any attributes previously associated with the characters in aRange.

If you need to do any of it conditionally, you could also enumerate the attributes and remove them one-by-one:

[originalMutableAttributedString enumerateAttributesInRange:originalRange
                                                    options:kNilOptions
                                                 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
                                                        [attrs enumerateKeysAndObjectsUsingBlock:^(NSString *attribute, id obj, BOOL *stop) {
                                                            [originalMutableAttributedString removeAttribute:attribute range:range];
                                                        }];
                                                    }];

According to the docs this is allowed:

If this method is sent to an instance of NSMutableAttributedString, mutation (deletion, addition, or change) is allowed.


Swift 2

If string is a mutable attributed string:

string.setAttributes([:], range: NSRange(0..<string.length))

And if you want to enumerate for conditional removal:

string.enumerateAttributesInRange(NSRange(0..<string.length), options: []) { (attributes, range, _) -> Void in
    for (attribute, object) in attributes {
        string.removeAttribute(attribute, range: range)
    }
}
Danyel answered 5/3, 2015 at 15:6 Comment(6)
This would be the logical answer - but the problem is that I'm working on the textStorage in NSTextView, and setting the new string resets the cursor position.Guttural
What about doing: let fullRange = NSMakeRange(0, mutableAttributedString.length) mutableAttributedString.setAttributes([:], range: fullRange)//clear attributesGuttural
Hey aaron, I just tested it and it for me it seems to work, clears all the attributes. I think the class behaves as though each character has a seperate dictionary... feel free to update your answer as I appreciate the help and I think it would be a bit short-handed to add my own :)Guttural
@james_alvarez Updated. Glad it works. Might be helpful to add this as a category on NSMutableAttributedString.Danyel
please correct me fi I'm wrong: string.length will not return a utf-16 value that NSRange expects. It should be string.utf16.count, right? You can verify by using emojis and other stranger characters.Reparable
@ParthMehrotra Yeah, that's correct for newer versions of swift. Upvoted your comment :)Danyel
G
5

swift 4:

I needed to remove the attributes from a textview in a reusable cell, they were transferred from one cell to another if the other was using the text property, so the text instead being only a text it was with the attributes from the previous cell. Only this worked for me, inspired by the accepted answer above:

iOS

let attr = NSMutableAttributedString(attributedString: (cell.textView?.attributedText)!)
    let originalRange = NSMakeRange(0, attr.length)
    attr.setAttributes([:], range: originalRange)
    cell.textView?.attributedText = attr
    cell.textView?.attributedText = NSMutableAttributedString(string: "", attributes: [:])
    cell.textView?.text = ""

macOS

textView.textStorage?.setAttributedString(NSAttributedString(string: ""))
Groundmass answered 12/1, 2018 at 7:1 Comment(1)
Thanks Cristi, I was dealing with the same issue and this worked perfectly. Do you know why the style from the other cell was coming over? I had my cells styled based on the section they were in and all the other styles were working correctly, just not the attributed text. The first cell shouldn't even be able to accept attributed text, since it had a .text propertyKamacite
T
2

How I remove and add attributes of a string in a tableView cellForRowAt indexPath. Create a new NSMutableAttributedString because it is immutable

// strike through on text
    let attributeString =  NSMutableAttributedString(string: "your string")
    attributeString.addAttribute(NSAttributedString.Key.strikethroughStyle, value: 2, range: NSMakeRange(0, attributeString.length))

    let removedAttributeString = NSMutableAttributedString(string: "your string")
    removedAttributeString.removeAttribute(NSAttributedString.Key.strikethroughStyle, range: NSMakeRange(0, attributeString.length))

    if (item.done == true) {
        // applies strike through on text
        cell.textLabel?.attributedText = attributeString

    } else {
        // removes strike through on text
        cell.textLabel?.attributedText = removedAttributeString
    }
Toccaratoccata answered 9/12, 2019 at 14:12 Comment(1)
this is the only thing that seemed to work for me , even setting the attributedText to nil didnt work !Lipetsk
S
1

NSAttributedString is immutable therefore you can't do much with an instance of it. One option is to use the string property of NSAttributedString, create a new NSMutableAttributedString with it and apply the desired attributes or make a mutableCopy of your NSAttributedString instance and modify the current attributes for ranges.

Suppuration answered 5/3, 2015 at 15:3 Comment(1)
See edits above - specifically I dont want to create a new NSMutableAttributedStringGuttural
H
0

First off, it would have to be an NSMutableAttributedString, as that has the method of removeAttribute(name: String, range: NSRange), and yes it looks like you would have to enumerate them and remove them.

Why not try this approach (Swift):

let s1 = <some NSAttributedString>
var s2 = NSMutableAttriutedString(string: s1.string())
Helve answered 5/3, 2015 at 15:11 Comment(1)
See edits above - specifically I dont want to create a new NSMutableAttributedStringGuttural
C
0
extension NSMutableAttributedString {
    func attributesClear() {
        let range = NSRange(location: 0, length: self.length)
        
        self.attributes.keys
            .forEach{
                self.removeAttribute($0, range: range)
            }
    }
}
Coracle answered 29/8, 2023 at 12:24 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.