NSBackgroundColorAttributeName in textView doesn't respect range if highlighted word is first of a line
Asked Answered
L

2

11

I'm implementing a "search in chat" feature and I want the searched word to be highlighted in messages. The problem, as described in title, is that if the word is the first of a line (long messages are clearly multi-line), the entire line is highlighted instead of the single word.

While debugging, I also tried to apply an underline instead of backgroundcolor, and the underline is correct. I can't figure out where's the problem. My app's chat is based on JSQMessagesViewController so I was thinking that the problem could be there.

[attributedString addAttribute:NSBackgroundColorAttributeName
                         value:backColor
                         range:wordRange];
[attributedString addAttribute:NSUnderlineStyleAttributeName
                         value:@(NSUnderlineStyleSingle)
                         range:wordRange];

cell.textView.attributedText = attributedString;

I don't post the code that calculates range because range is correct; in fact, if I look at attributedString's preview in debug (where I assign it to cell.textView.attributedText) I can see that only the word is highlighted, and not all the string.

Here's an example:
Example

Logotype answered 6/9, 2019 at 10:44 Comment(0)
T
7

Seems like a bug in the system framework, so probably best bet would be to add one more line of code, setting background color back to "unchanged", directly after the last character where background should be changed. Good luck!

Tessitura answered 16/1, 2020 at 9:26 Comment(6)
What is the "unchanged" value? There are no other attributes set on the attributedString and printing its description the range of NSBackgroundColorAttributeName is correct.Antinode
@Antinode If this is bug in system then setting for range following part of string for which we want background changed back to its previous value seems to be the only viable option. Most correct would be author (probably Apple) fixing its own framework but it will take some time for sure. If my fix is meant to work of course we need to add attributes to string in order, from its beginning towards endTessitura
There is no previous value, it's "transparent".Antinode
@Antinode Then try to set it to clearColor (or in fact any color with alpha zero)Tessitura
@Antinode Ok, this also may not work. Depends on how exactly adding background color works internally. It should remove any layer previously there as background. But if it’s just adding one more layer on top then setting color of the cell (in photos above blue or light grey) should be correct fix for thatTessitura
Setting the NSBackgroundColorAttributeName to clearColor with a length of 1 right after the end of the highlighted string solved the inconsistent highlighting problem. So I've tried also to `clearColor' the whole 'attributedString' before adding highlighting and it also worked correctly.Antinode
S
1

After taking a look at official documentation, I found that there are three methods which do similar action.

  1. setAttributes:range:
  2. addAttribute:value:range:
  3. addAttributes:range:

So, possibly, other method, for example setAttributes:range:, may fix you problem.

[attributedString setAttributes:@{NSBackgroundColorAttributeName : backColor}
                          range:wordRange];

cell.textView.attributedText = attributedString;

Furthermore, there is a similar question on stackoverflow. Maybe this answer will help you.

NSDictionary *stringAttributes = [NSDictionary dictionaryWithObject:(id)backColor.CGColor
                                                             forKey:(id)kCTBackgroundColorAttributeName];
[attributedString addAttributes:stringAttributes 
                          range:wordRange];

cell.textView.attributedText = attributedString;

Update #1

Just found Attributed String Programming Guide.
It says,

All of the methods for changing a mutable attributed string properly update the mapping between characters and attributes, but after a change some inconsistencies can develop.

Perhaps, the above reason causes your problem... I'm not sure can it help or no, but there is example:

NSMutableAttributedString *string; // assume string exists
NSRange selectedRange; // assume this is set

NSURL *linkURL = [NSURL URLWithString:@"http://www.apple.com/"];

[string beginEditing]; // ★★★ Apple uses beginEditing ★★★
[string addAttribute:NSLinkAttributeName
               value:linkURL
               range:selectedRange]; 
[string addAttribute:NSForegroundColorAttributeName
               value:[NSColor blueColor]
               range:selectedRange];
[string addAttribute:NSUnderlineStyleAttributeName
               value:[NSNumber numberWithInt:NSSingleUnderlineStyle]
               range:selectedRange];
[string endEditing]; // ★★★ and endEditing ★★★
Scad answered 22/1, 2020 at 2:27 Comment(3)
kCTBackgroundColorAttributeName (※2nd approach) also didn't help you?Postoperative
No, I've tried to do all 3 approaches together and print the debug information of the resulting attributedString and they were the same.Antinode
Updated my answer, maybe beginEditing/endEditing will be able to fix the problem.Postoperative

© 2022 - 2024 — McMap. All rights reserved.