Search code examples
ioscocoansmutableattributedstringtextkitnstextstorage

Which replacement method is called when editing an attributed string?


NSMutableAttributedString defines two string replacement methods:

func replaceCharacters(in range: NSRange, 
                       with str: String)

and

func replaceCharacters(in range: NSRange, 
                with attrString: NSAttributedString)

I have created a subclass of NSTextStorage which in its turn is a subclass of NSMutableAttributedString. In that subclass I overrode both of the above methods.

To my surprise, the latter method (which passes an attributed replacement string) is never called when I type or paste something in my text view. It's only the plain string replacement method that gets called each time.

This leads me to the question:
Based on which rules are these two methods actually called, when the user is editing the text in a text view?

(I need to perform different operations in my text storage, depending on whether the attributed replacement string contains a specific kind of text attachment. But if the replacement method for the attributed string is never called, I see no way how to make this distinction. 😕)


Solution

  • Function

    func replaceCharacters(in range: NSRange, 
                    with attrString: NSAttributedString)
    

    is actually never called.

    From documentation (below) it is clear that one should use combination of replaceCharactersInRange:withString: followed by a call setAttributes:range:

    /* Note for subclassing NSTextStorage: NSTextStorage is a semi-abstract subclass of NSMutableAttributedString. It implements change management (beginEditing/endEditing), verification of attributes, delegate handling, and layout management notification. The one aspect it does not implement is the actual attributed string storage --- this is left up to the subclassers, which need to override the two NSMutableAttributedString primitives in addition to two NSAttributedString primitives:

    • (NSString *)string;
    • (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range;

    • (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;

    • (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range; These primitives should perform the change then call edited:range:changeInLength: to get everything else to happen. */

    Make sure you have custom implementation of these methods.

    And this is also how AppKit implements NSTextView:

    enter image description here