NSAttributedString Color Test

11,185

Solution 1

Objective-C is a strict superset of C. Objective-C objects live on the heap and are kept track of via pointer. The net effect of this is that the test:

[attributes objectForKey:NSForegroundColorAttributeName] == 
[UIColor colorWithRed:1 green:0 blue:0 alpha:1]

tests identity rather than equality. That is, rather than testing whether two objects have the same value you're testing whether they're the same physical object, sitting at the same address in memory. C doesn't allow operator overloading so the == operator has exactly the same meaning in Objective-C as in C. You're comparing two pointers. That they happen to point to Objective-C objects is neither here nor there.

You probably want:

[[attributes objectForKey:NSForegroundColorAttributeName] isEqual:
    [UIColor colorWithRed:1 green:0 blue:0 alpha:1]]

Which will allow the UIColor object to apply whatever test it considers will establish equality.

The only potential caveat is that UIColor considers colours to be equal only if they're defined relative to the same colour space and have the same coefficients. Assuming you're always defining all your colours in RGBA that shouldn't make a difference.

Solution 2

Try testing for the NSForegroundColorAttributeName object in the attributes dict instead of NSFontAttributeName like so:

NSAttributedString *selectedString = [attributedString attributedSubstringFromRange:selectedRange];
[selectedString enumerateAttributesInRange:NSMakeRange(0, [selectedString length]) 
                                   options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired 
                                usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
    UIColor *fgColor = [attributes objectForKey:NSForegroundColorAttributeName];       
    if ([fgColor isEqual:[UIColor redColor]]) {
        NSLog(@"detected red. set selected");
        [self.redButton setSelected:YES];
    } else {
        NSLog(@"detected not red. do not set selected");
        [self.redButton setSelected:NO];
    }
}
Share:
11,185
DenVog
Author by

DenVog

Attempting to restore a sense of child-like wonder one iPhone app at a time.

Updated on August 05, 2022

Comments

  • DenVog
    DenVog over 1 year

    What is the correct way to compare, or test, for a particular color attribute of an NSAttributed string?

    As an example, I'd like to know if the text selection has red text. I have tried several approaches, as you can see below, but none of them ever results in a match. I see the text turn red on screen, and logging the attribute returns: UIDeviceRGBColorSpace 1 0 0 1

    - (BOOL)isRedWithAttributes:(NSDictionary *)attributes
    {
        BOOL isRedWithAttributes = NO;
        if (attributes != nil)
        {
    // if ( [attributes objectForKey:NSForegroundColorAttributeName] == [UIColor redColor] )    
    // if ( [attributes objectForKey:NSForegroundColorAttributeName] == @"UIDeviceRGBColorSpace 1 0 0 1" )
    
           if ( [attributes objectForKey:NSForegroundColorAttributeName] == [UIColor colorWithRed:1 green:0 blue:0 alpha:1] )
           {
                isRedWithAttributes = YES;
            }
            else
            {
                isRedWithAttributes = NO;
            }
        }
        return isRedWithAttributes;
    }
    

    Here is how I pass the attribute for the test:

    NSAttributedString *selectedString = [attributedString attributedSubstringFromRange:selectedRange];
            [selectedString enumerateAttributesInRange:NSMakeRange(0, [selectedString length]) 
    options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
                                  usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop)
            {
                UIFont *fontFace = [attributes objectForKey:NSFontAttributeName];       
                BOOL isRedWithAttributes = [fontFace isRedWithAttributes:attributes];
                if (isRedWithAttributes)
                {
                    NSLog(@"detected red. set selected");
                    [self.redButton setSelected:YES];
                }
                else
                {
                    NSLog(@"detected not red. do not set selected");
                    [self.redButton setSelected:NO];
                }
    }
    

    I don't think it matters, but for completeness, here is how I set the text to be red.

    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.synopsisTextView.attributedText];
    [attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:selectedRange];
    self.synopsisTextView.attributedText = attributedString;
    
  • DenVog
    DenVog over 11 years
    Awesome. Thanks for the reply, and reasoning behind it. Also works with just the color name [[attributes objectForKey:NSForegroundColorAttributeName] isEqual:[UIColor redColor]]