Question

I'm calling the following selector on an existing NSAttributedString with no kCTFontAttributeName ranges:

[attributedString enumerateAttribute:(NSString *) kCTFontAttributeName
                             inRange:NSMakeRange(0, [attributedString length])
                             options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
                          usingBlock:^(id value, NSRange range, BOOL *stop) {
    NSLog(@"Attribute: %@, %@", value, NSStringFromRange(range));
}];

and I get the output below, but I would expect to get no output. Suggestions?

Attribute: (null), {0, 27}
Attribute: (null), {27, 1}
Attribute: (null), {28, 1}
Attribute: (null), {29, 1}
Attribute: (null), {30, 1}
Was it helpful?

Solution

The short answer? -enumerateAttribute:inRange:options:usingBlock: doesn't do what you (or I, originally) thought it does.

From the name, you might assume it only enumerates over ranges of the receiver that contain the given attribute. This is not the case. It always enumerates over the entire string. It calls the block for each run it encounters. The value passed into the block is set to the value the given attribute for that run. If the current run doesn't contain the given attribute, it passes nil for value.

Thus, for a string that does not contain the given attribute, it will still fire the block, but the value will always be nil. For a string that that is completely covered by the the given attribute (with the same value), you would expect the block to fire once with value being equal to that attribute's value in the string. For a string that is partially covered by the given attribute, you would expect the block to fire multiple times, sometimes with a value of nil, and sometimes with a value equal to that of the attribute.

Hope that helps. It took me a while to look at it from the correct direction, also.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top