6

UPDATE:

I created a really simple standalone project to demonstrate the bug. If anyone would like to pull same and see if they can spot where I've gone wrong, I'd sure appreciate it. There's not much code to look through. Public repo here: https://github.com/reidnez/NSAttributedStringBugDemo

I'm having a very strange issue here: I have a tableview. Each cell has a title label with 1-3 words, and a keywords label with several CSV keywords. I also have a search bar. The requirement is that as the user types into the search bar, any partial matches on both the title and keywords for each cell are shown highlighted. Screenshots:

Everything looks as it should searching on "Fa"

"an" highlights keyword, but not title

First image is A-Okay. In the second image, the "an" of the title label should be highlighted. But, as you can see, not so much...

This works perfectly fine on the "keywords" label, as you can see above. The attributed strings for both of these labels are created by a category I wrote (code below). The same method is called on both strings, and appears to behave the same from what the debugger is telling me. The UI tells a different story.

I have stepped through the debugger numerous times, and in all cases, the attributed string appears to have been configured correctly. I have also verified that something else is not calling [tableView reloadData] and that no other place in my code is overwriting the label's value. This is how matching on "an" for "Fang" looks in the debugger, just before the cell is returned at the end of cellForRowAtIndexPath:

(lldb) po customCell.entryTitleLabel.attributedText
F{
}an{
NSBackgroundColor = "UIDeviceRGBColorSpace 0.533333 0.835294 0.156863 1";
}g{
}

Looks good to me...that is exactly what I want. But when the cell renders, there are NO highlights to be seen! Weirder yet, as an experiment I tried setting the label to a completely arbitrary attributedString that I created right in cellForRow:

NSMutableAttributedString *fake = [[NSMutableAttributedString alloc] initWithString:@"Fang"];
            [fake addAttribute:NSBackgroundColorAttributeName value:MATCH_TEXT_HILIGHT_COLOR range:NSMakeRange(1, 2)];
            customCell.entryTitleLabel.attributedText = fake;

This, too fails. No highlighting at all...but I CAN highlight any substring in the range of {0, 1} to {0, fake.length} and it behaves as expected. Again, it seemingly refuses to highlight any substring that does not begin at index 0--but only for the title label.

Am I losing my mind? What am I missing?

Below is my category...but I am fairly confident the problem does not lie here, because it functions perfectly for the keywords string, and (again) the attributes appear to be set correctly just before the cell returns:

-(void)hilightMatchingSubstring:(NSString*)substring color:(UIColor*)hilightColor range:(NSRange)range
{
    if ([self.string compare:substring options:NSCaseInsensitiveSearch] == NSOrderedSame) {
        [self addAttribute:NSBackgroundColorAttributeName value:hilightColor range:NSMakeRange(0, self.length)];
        return;
    }

    // Sanity check. Make sure a valid range has been passed so that we don't get out-of-bounds crashes. Default to return self wrapped in an attributed string with no attributes.
    NSRange selfRange = NSMakeRange(0, self.length);
    if (NSIntersectionRange(selfRange, range).length == 0) {
        NSLog(@" \n\n\n*** Match range {%lu, %lu} does not intersect main string's range {%lu, %lu}. Aborting *** \n\n\n", (unsigned long)range.location, (unsigned long)range.length, (unsigned long)selfRange.location, (unsigned long)selfRange.length);
        return;
    }

    if (substring.length > 0) {
        NSRange movingRange = NSMakeRange(range.location, substring.length);
        if (NSMaxRange(movingRange) > self.length) {
            return;
        }

        NSString *movingString = [self.string substringWithRange:movingRange];

        while (NSMaxRange(movingRange) < NSMaxRange(range)) {
            if ([movingString compare:substring options:NSCaseInsensitiveSearch] == NSOrderedSame) {
                [self addAttribute:NSBackgroundColorAttributeName value:hilightColor range:movingRange];
            }
            movingRange = NSMakeRange(movingRange.location + 1, substring.length);
            movingString = [self.string substringWithRange:movingRange];
        }
    } // This is fine...string leaves properly attributed.
}
Reid
  • 1,109
  • 1
  • 12
  • 27
  • Downvote with no comment? Gee, thanks. Super constructive! I won't do that again...whatever it is I did? – Reid Sep 20 '14 at 22:34
  • 2
    I'm seeing the same thing with the labels of table view cells. I can underline the start of the text but not the middle. I suggest making a simple little test app and submitting it with a bug report to Apple. – rmaddy Sep 20 '14 at 22:40
  • "It's Apple's bug" is always my answer of last resort...I really have to prove to myself that that's the case (much more likely that little-ole-me made a mistake somewhere). But if you have experienced a similar issue, it may actually be the right conclusion. The standalone test-app is a great idea, I will do that. Thanks! – Reid Sep 20 '14 at 22:42
  • 1
    I filed a bug report in mid-July against iOS 8 beta 3. It's still an issue. The more Apple hears from other debs, the more likely it will get fixed. – rmaddy Sep 20 '14 at 22:47
  • I followed your suggestion and made a standalone project, as small and simple as possible...guess what, same bug! I added a link to the repo in my answer in case anyone wants to pull and see if they can find where I've gone wrong. :) – Reid Sep 20 '14 at 23:16
  • Oh, and also filed a bug report with link to the repo. It may in fact be my bug, but I sure as hell can't find it. The fact that it occurs identically in a stripped-down demo project is compelling. – Reid Sep 20 '14 at 23:35
  • Check my answer here http://stackoverflow.com/questions/25932632/displaying-nsmutableattributedstring-on-ios-8/27316651#27316651 – Kamran Khan Dec 05 '14 at 13:11

1 Answers1

2

Thanks for writing this up... Thought I was going crazy too!

I came up with a workaround (read: hack) whilst we wait for something official from Apple.

NSDictionary *hackAttribute = [NSDictionary dictionaryWithObjectsAndKeys:
                           [UIColor clearColor], NSBackgroundColorAttributeName, nil];

NSMutableAttributedString *attributedText =
    [[NSMutableAttributedString alloc] initWithString:@"some text..."];
    [attributedAddressText setAttributes:hackAttribute range:NSMakeRange(0, attributedText.length)];
// Then set your other attributes as per normal

Hope that helps.

Sam Wan
  • 176
  • 1
  • 5
  • Thanks! I actually gave up on fixing this long ago and started working on other things. But...as soon as I get home, I will test this out and (assuming it works) accept your answer. – Reid Jan 09 '15 at 16:59
  • @ReidBelton: lemme know how it goes :) – Sam Wan Jan 20 '15 at 01:59