Declare a color as property

I’m missing something here:

property NSColor : class “NSColor”

property gString1 : missing value – OK
property gString2 : “Hello World” – OK
property gColor1 : NSColor’s whiteColor – OK
property gColor2 : NSColor’s colorWithCalibratedRed_green_blue_alpha_(0.761364,1,1,1)

error: class “NSColor” doesn’t understand the colorWithCalibratedRed_green_blue_alpha_ message.

colorWithCalibratedRed_green_blue_alpha_ is a +class method.

So what’s wrong?

And by Mac’s gods, what is this new error?

Where are you now,

setRGBColor (myRgb);
paintRect (myRect)

of the wild ages of QuickDraw.

This is too long to be correct – and actually it doesn’t work:

property gColorList : missing value

property color0 : missing value
property color1 : missing value
.

set my gColorList to NSMutableDictionary's dictionaryWithObjectsAndKeys_(color0,"0",color1,"1",.,missing value)
set my color0 to NSColor's colorWithCalibratedRed_green_blue_alpha_(0.761364,1,1,1)
set my color1 to NSColor's colorWithCalibratedRed_green_blue_alpha_(0.5272,0.53326,0.53326,1)
.

Color wells 0-9 are bound by value to these properties color0…color9. When updated by the color picker, the color well changes its color, but the keyed value of the dic remains the same.

I really need help. It’s one of those days where everything I thought I knew shatters into small pieces.

Have you tried global values instead of properties? They’d be a bit more flexible in this case.

And do you need them to be saved when you quit?

Browser: Safari 531.22.7
Operating System: Mac OS X (10.6)

The problem is the binding, and the saving with a keyed archiver. Are they possible with globals instead of properties?

Well, if you bind the properties to the Shared User Defaults Controller in IB and use “NSKeyedUnarchiveFromData” as the Value Transformer, then the NSColor values will be converted to and from the user defaults at quit and launch.

Once they have been read back in at launch, they should be standard NSColor objects and you should be able to modify them at will.

Browser: Safari 531.22.7
Operating System: Mac OS X (10.6)

I read (during hours) the way of Archiving/Unarchiving data, dictionaries and array. The result was just turning my neurons to jam.

I intend to save a matrix of cells to a file, each cell having a color code, and to save along with this matrix a set of colors and a set of text legends.

I made this two times in my app, and I just don’t know how to do this time. The colors and legends can be modified by user, then he composes its map and save the whole thing to a file. It seems easy to conceptualize, but the implementation is not SO trivial – of course I’m playing with NSColor, a property rarely bindable among objects, not storable with a dictionary (like user defaults), and NSColorWell, which has no cell companion class. I just discovered the limits of Cocoa ” from ASOC of course. I persist to believe that a custom cell may be the solution, but Objective-C is a small step for a C programer, and a giant leap for me.

Well, like I said, NSColor and NSUrl values are storable in user defaults if you use an coder/decoder. Works like a charm, maybe you see this as something more complex than it is.

If I can suggest something, did you read Shane Stanley’s book on ASOC? This is worth 10 times its price in knowledge and answers, and I would not be where I am without this book. In there there is info on how to store NSColor values, and probably could be adaptable to a custom file.

Good luck!

Browser: Safari 531.22.7
Operating System: Mac OS X (10.6)

All has begun with buying Shane Stanley’s book ! Maybe I shall read it again, and again. It is more digestible than Apple’s documentation :slight_smile:

Thank you, leonsimard!

It works.

I know a feeling more uncomfortable than having a code that does not work: it is to have a code that works without knowing exactly why. :confused:

No more dics, no more keys, minimal bindings, whole view objects are saved to file as keyed archive. 30 code lines less. Simplicity is complicated to achieve.

This post is closed.

If you want to know more, read up on the NSCoding protocol. When you archive an object, its encodeWithCoder: method gets called, which in turn calls the encodeWithCoder: method on all its instance variables, and so on recursively with the objects represented by those variables. Unarchiving uses initWithEncoder: to reverse the process.

So it works as long as the object and all its contained objects support the NSCoding protocol – most classes do, but you have to add the code to do it yourself if they don’t (NSObject is an example).

Shane,

Can you tell me why the background color of the cells of a matrix wouldn’t be saved when archiving an NSMatrix (the cells are NSButtonCells). NSButtonCell, NSCell, and NSMatrix all conform to NSCoding. Every other property of the cells, as far as I can tell, is saved, why not the background color?

Ric

No I don’t. Can you subclass a color and override encodeWithEncoder: to see if it gets called?

I don’t think I know how to subclass a color.

Yeah,I see the problem. What about a category that overrides encodeWithEncoder: (and calls it on super)? Probably not a brilliant thing to do normally, but just for testing…

I’ve tried two things to solve this problem, one of which sort of works and the other one doesn’t. After some experimenting, it seems that the problem of not archiving the background color is in the NSButtonCell class’s implementation of encoding. One method i tried was to make a category on NSButtonCell – this saves the color, but the other buttons in the program (in open and save panels) end up with the same bezel style or lack of border that the buttonCell I’m creating has. I also get some error messages. This is what I tried:

@implementation NSButtonCell (ColorSaving) 

- (void)encodeWithCoder:(NSCoder *)encoder {
    NSLog(@"Got to the encoder");
    [super encodeWithCoder:encoder];
    [encoder encodeObject:self.backgroundColor forKey:@"bColor"]; 
    [encoder encodeInteger:self.bezelStyle forKey:@"bStyle"];
    [encoder encodeBool:self.isBordered forKey:@"bordered"];
    [encoder encodeInteger:self.controlSize forKey:@"cSize"];
}

- (id)initWithCoder:(NSCoder *)decoder {
    NSLog(@"Got to the decoder");
    [super initWithCoder:decoder];
    self.backgroundColor = [decoder decodeObjectForKey:@"bColor"];
    self.bezelStyle = [decoder decodeIntegerForKey:@"bStyle"];
    [self setBordered:[decoder decodeBoolForKey:@"bordered"] ];
    self.controlSize = [decoder decodeIntegerForKey:@"cSize"];
    return self;
}

@end

The error messages that I get are (I get a whole bunch of these):

That’s why I tried to put in the bezel style and control size lines in, but they don’t make any difference. The other strange effect I get is that I can get a colored in cell even if it has a border --I don’t see why adding this category would change that. Also, why would the other buttons in the open and save panels have no border like the cell I create in code?

The other approach I tried was to subclass NSButtonCell. I thought this would get rid of the problem of having other button cells in the app change their appearances. The problem is that the encodeWithCoder and initWithCoder methods that I overrode never get called. Here is what I tried:

@implementation MyButtonCell : NSButtonCell

+(MyButtonCell *)cellWithTag:(NSUInteger) tag color:(NSColor *)color title:(NSString *)title {
  MyButtonCell  *myCell = [[NSButtonCell alloc]init];
    [myCell setBordered:NO];
    [myCell setTag:tag];
    [myCell setTitle:title];
    [myCell setBackgroundColor:color];
    [myCell setBezelStyle:NSSmallSquareBezelStyle];
    return myCell;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    NSLog(@"Got to the encoder");
    [super encodeWithCoder:encoder];
    [encoder encodeObject:self.backgroundColor forKey:@"bColor"]; 
}

- (id)initWithCoder:(NSCoder *)decoder {
     NSLog(@"Got to the decoder");
  [super initWithCoder:decoder];
   self.backgroundColor = [decoder decodeObjectForKey:@"bColor"];
    return self;
}
@end

I added a convenience creator method for testing, and that worked fine, but the other methods don’t get called. Any ideas why not?

Ric

Wow – I left this post behind when it became interesting.

Ric, is this the implementation of an object subclass? that is, what stands in a .m file?

a) you call super to get the superclass inherited method, ok?
b) [self setBordered:[decoder decodeBoolForKey:@“bordered”] ]; // is a call to a superclass method, ok?
c) self.controlSize = [decoder decodeIntegerForKey:@“cSize”]; // is the access to a superclass instance variable, ok?

And when you create a subclass, you may (or not) override some methods, declare your own special variables, and so on, like in any OOP language, just to follow these strange rules (for me) of [[[[something]]]] inside these hideous (for me)
{
}
?
I have to try this, at the cost of getting pages of compiler and linker errors :stuck_out_tongue:

And of course you can specify in IB (by filling the “class” field) that an object’s instantiation should call your methods?

I ask before doing, so I won’t flood the site with edgy posts. :wink:

Not here…

I’m not sure what you mean here – the top code I posted is a category not a subclass. The second piece of code is a subclass of NSButtonCell.

a) Yes, using super calls the superclass’s version of a method.
b) I think so – I’m not sure why I have to use self here, I get an error (“setBordered undeclared”) if I don’t.
c) Yes, through the setter and getter

Ric

Well, I think I’ve figured out how to do this (almost) with a category on NSButtonCell. I made another small program to archive a regular button (with no category or subclass) and used NSKeyedArchiver’s method, setOutputFormat, to set the format to XML so I could look at it in the Property List Editor. Seeing what happens with various kinds of buttons and extrapolating from the code in NSButtonCell.m at Cocotron led me to this code:

@implementation NSButtonCell (ColorSaving) 

- (void)encodeWithCoder:(NSCoder *)coder {
    [super encodeWithCoder:coder];
    [coder encodeObject:self.backgroundColor forKey:@"bColor"];
    [coder encodeInteger:self.isBordered forKey:@"NSButtonFlags"];
    if (self.bezelStyle < 8) {
        [coder encodeInteger:(self.bezelStyle) + (self.showsBorderOnlyWhileMouseInside<<3) forKey:@"NSButtonFlags2"];
    }else{
    int bezelFlag = (self.bezelStyle + 24) + (self.showsBorderOnlyWhileMouseInside<<3);
    [coder encodeInteger:bezelFlag forKey:@"NSButtonFlags2"];
    }
}

- (id)initWithCoder:(NSCoder *)decoder {
    [super initWithCoder:decoder];
    self.backgroundColor = [decoder decodeObjectForKey:@"bColor"];
    int flags2 = [decoder decodeIntegerForKey:@"NSButtonFlags2"];
    self.bezelStyle=(flags2&0x7)|((flags2&0x20)>>2);
    [self setShowsBorderOnlyWhileMouseInside:flags2&0x8 ];
    [self setBordered:[decoder decodeIntegerForKey:@"NSButtonFlags"]];
    return self;
}
@end

In my previous attempt, I made up my own keys for things that already existed, so buttons in the open and save dialogs couldn’t find their keys and defaulted to a square button type. It turns out that quite a few of the ivars in a button cell are encoded into a couple of flags using bitwise manipulations. I’ve parsed out some of that so the tag, bezel style, isBordered, backgroundColor and showsBorderOnlyWhileMouseInside all work correctly in the buttons that I create and the buttons in the open and save dialogs are back to normal with one exception – the disclosure button that expands the view in the save panel doesn’t show its image or alternativeImage.

Ric

I found out the problem with my subclass of NSButtonCell not calling its version of encodeWithCoder: or initWithCoder: but I don’t understand it. Substituting the bottom code for the top in the code I posted above makes it work:

 MyButtonCell *myCell = [[NSButtonCell alloc]init];

this doesn’t:

 MyButtonCell *myCell = [[super alloc]init];

My class’s superclass is NSButtonCell, so why are those to lines different?

Ric