1

I have an NSTextView object that has been added through interface builder. I am able to change the background color of the text segment that has been selected as follows.

- (void)emphasizeText {
    NSColor *c = [colorWell.color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; //colorWell is an NSColorWell object that has been added through interface builder
    NSRange selection = textView1.selectedRange; //textView1 is an NSTextView object that has been added through interface builder
    [textView1.textStorage addAttributes:@{NSBackgroundColorAttributeName:c} range:selection];
}

Yet, I have trouble removing the added attribute (NSBackgroundColorAttributeName) from the selected text segment. In reference to this topic, I have the following code.

- (void)deemphasizeText {
    NSMutableAttributedString *aStr = [textView1.attributedString mutableCopy];
    NSRange selection = textView1.selectedRange;
    [aStr enumerateAttribute:NSBackgroundColorAttributeName inRange:selection options:0 usingBlock:^(id value,NSRange range,BOOL *stop) {
        if (value) {
            [aStr removeAttribute:NSBackgroundColorAttributeName range:range];
        }
    }];
}

And nothing changes though the application won't crash. I have done several variations like

- (void)deemphasizeText {
    NSRange selection = textView1.selectedRange;
    NSMutableAttributedString *aStr = [[NSMutableAttributedString alloc] initWithAttributedString:(NSMutableAttributedString *)[textView1.textStorage attributedSubstringFromRange:selection]];
    [aStr enumerateAttribute:NSBackgroundColorAttributeName inRange:NSMakeRange(0,aStr.length) options:0 usingBlock:^(id value,NSRange range,BOOL *stop) {
        if (value) {
            [aStr removeAttribute:NSBackgroundColorAttributeName range:range];
        }
    }];
}

Again the selected segment doesn't change. What am I doing wrong?

Muchos thankos.

Community
  • 1
  • 1
El Tomato
  • 6,479
  • 6
  • 46
  • 75

1 Answers1

0

First, your -deemphasizeText methods are explicitly working with a copy of the text storage mutable string. One would not expect modifying that copy to modify the original.

Second, you should bracket all modifications of the text storage with calls to -beginEditing and -endEditing. In fact, you really ought to call -shouldChangeTextInRange:replacementString: and -didChangeText on the text view around changes.

Finally, there's no need to enumerate the attributed string and remove the attribute from only the ranges where it's present. Just remove the attribute for the whole selection range.

So:

- (void)emphasizeText {
    NSColor *c = [colorWell.color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
    NSRange selection = textView1.selectedRange;
    if ([textView1 shouldChangeTextInRange:selection replacementString:nil])
    {
        NSTextStorage* storage = textView1.textStorage;
        [storage beginEditing];
        [storage addAttributes:@{NSBackgroundColorAttributeName:c} range:selection];
        [storage endEditing];
        [textView1 didChangeText];
    }
}

- (void)deemphasizeText {
    NSRange selection = textView1.selectedRange;
    if ([textView1 shouldChangeTextInRange:selection replacementString:nil])
    {
        NSTextStorage* storage = textView1.textStorage;
        [storage beginEditing];
        [storage removeAttribute:NSBackgroundColorAttributeName range:selection];
        [storage endEditing];
        [textView1 didChangeText];
    }
}

Other things to consider:

  • adjusting the selection range to be appropriate for the modification you're doing. Call -rangeForUserCharacterAttributeChange; check if its location is NSNotFound and abort if so; check if it's different from the current selectedRange and, if so, set the selectedRange to match; then operate on that range.
  • supporting multiple selection ranges by using -rangesForUserCharacterAttributeChange and -shouldChangeTextInRanges:replacementStrings:.
Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Hmm... I don't know where I got the idea about enumerateAttribute, which, I guess, I don't have to use. I feel a bit silly. Thanks, Ken. – El Tomato Jan 03 '16 at 22:50