Цветовой тест NSAttributedString

Каков правильный способ сравнения или проверки определенного цветового атрибута строки NSAttributed?

Например, я хотел бы знать, есть ли в выделенном тексте красный текст. Я пробовал несколько подходов, как вы можете видеть ниже, но ни один из них не приводит к совпадению. Я вижу, что текст становится красным на экране, и журнал возвращает атрибут: 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;
}

Вот как я передаю атрибут для теста:

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];
            }
}

Я не думаю, что это имеет значение, но для полноты картины я сделал текст красным.

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.synopsisTextView.attributedText];
[attributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:selectedRange];
self.synopsisTextView.attributedText = attributedString;

person DenVog    schedule 06.12.2012    source источник


Ответы (3)


Objective-C — это строго надмножество C. Объекты Objective-C живут в куче и отслеживаются с помощью указателя. Чистый эффект этого заключается в том, что тест:

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

проверяет тождество, а не равенство. То есть вместо того, чтобы проверять, имеют ли два объекта одинаковое значение, вы проверяете, являются ли они одним и тем же физическим объектом, находящимся по одному и тому же адресу в памяти. C не допускает перегрузки операторов, поэтому оператор == имеет точно такое же значение в Objective-C, как и в C. Вы сравниваете два указателя. То, что они указывают на объекты Objective-C, ни здесь, ни там.

Вы, вероятно, хотите:

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

Это позволит объекту UIColor применить любой тест, который, по его мнению, установит равенство.

Единственное потенциальное предостережение заключается в том, что UIColor считает цвета равными только в том случае, если они определены относительно одного и того же цветового пространства и имеют одинаковые коэффициенты. Предполагая, что вы всегда определяете все свои цвета в RGBA, это не должно иметь значения.

person Tommy    schedule 07.12.2012
comment
Потрясающий. Спасибо за ответ и аргументацию. Также работает только с названием цвета [[attributes objectForKey:NSForegroundColorAttributeName] isEqual:[UIColor redColor]] - person DenVog; 07.12.2012

Попробуйте протестировать объект NSForegroundColorAttributeName в словаре атрибутов вместо NSFontAttributeName следующим образом:

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];
    }
}
person chown    schedule 07.12.2012

Вам нужно будет создать метод -isEqualToColor: для сравнения. UIColor наследует только -isEqual: от NSObject, и это рассматривает два экземпляра UIColor как равные, если их хэши равны.

Такой метод довольно легко построить, вы просто получаете значения RGB+A, независимые от цветового пространства, и цвета равны, если все эти значения одинаковы.

Если бы вы были ленивы, вы бы использовали CGColorEqualToColor, но это не сообщает о равенстве, если один — серый цвет (2 компонента), а другой — цвет RGBA (4 компонента).

Это, вероятно, причина, по которой Apple не внедрила метод -isEqualToColor:, потому что мнения сильно разнятся, должно ли количество компонентов играть роль или это только значения RGB. Поэтому я рекомендую вам найти собственное определение и соответствующим образом провести сравнение.

PS: В некоторых случаях вы можете обнаружить, что isEqual работает, потому что UIColor хранит кеш некоторых стандартных цветов, и если, например. если вы запросите красный, вы получите тот же экземпляр. Один и тот же экземпляр означает один и тот же хеш и, следовательно, «равно». Но, как я уже говорил, это зависит от реализации, это не очень хороший стиль.

person Cocoanetics    schedule 21.12.2012