Domanda

I have a class with UIColor property named color and I want to set this property by string:

[label setValue:@"1.0 0.5 0.0 1.0" forKey:@"color"];

I know I need to convert the string to UIColor. I noticed that KVC calls a method named "componentRGBA" which is where I want to perform the conversion. So I added a category method on NSString:

-(UIColor*) componentRGBA
{
    CIColor* ciColor = [CIColor colorWithString:self];
    UIColor* uiColor = [UIColor colorWithCIColor:ciColor];
    return uiColor;
}

The method is called. However, self does not seem to be a valid NSString object because the call to colorWithString: crashes with EXC_BAD_ACCESS and so does every attempt of sending self an NSObject message (class, description, etc).

My suspicion is that the method signature of componentRGBA is not correct and therefore self isn't actually the string object. Though I could not find any reference by googling this method.

How do I properly implement componentRGBA so I can perform the color conversion automatically when a UIColor property is set to a NSString* value via KVC?

Update:

Interestingly, when I do this in the componentRGBA method:

CFShowStr((__bridge CFStringRef)self);

I receive the message:

This is an NSString, not CFString

So it's supposed to be an NSString* yet I can't call any of its methods without crashing.

This simple test for example crashes:

NSLog(@"self = %@", [self description]);

The crash is in objc_msgSend with code=1 and address=0xffffffff (address varies from time to time).

In addition when I don't implement componentRGBA then KVC fails with the following message:

-[__NSCFConstantString componentRGBA]: unrecognized selector sent to instance 0xc48f4
È stato utile?

Soluzione

This might be only of academic interest, as you probably don't want to rely on an undocumented method, but the following implementation seems to work:

// This structure is returned by the (undocumened) componentRGBA
// method of UIColor. The elements are "float", even on 64-bit,
// so we cannot use CGFloat here.
struct rgba {
    float r, g, b, a;
};

@interface UIColor (ComponentRGBA)
-(struct rgba) componentRGBA;
@end

@interface NSString (ComponentRGBA)
-(struct rgba) componentRGBA;
@end

@implementation NSString (ComponentRGBA)
-(struct rgba) componentRGBA
{
    CIColor* ciColor = [CIColor colorWithString:self];
    UIColor* uiColor = [UIColor colorWithCIColor:ciColor];
    return [uiColor componentRGBA];
}
@end

I figured this out with the help of the sample project of your (now deleted) question KVC: what does the 'componentRGBA' method do when setting a color property?. The key point was that (as one could see by inspecting the stack backtrace) the componentRGBA method is called via objc_msgSend_stret(), which means that it returns a struct and not some id.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top