views:

51

answers:

2

Given all the complex things I seem to cover every day, this appears to be a "what the heck am I doing wrong that seems to simple?" scenario!

I would like to subclass an NSTextField to change the background color and text color. For simplicity sake (and to help anyone who hasn't ever subclassed anything before), here is the example of my (simplified) subclass MyNSTextFieldSubclass...

Step 1:

Create the subclass file:

First the header file

@interface MyTextFieldSubclass : NSTextField {
}

@end

And the method file

@implementation MyTextFieldSubclass

-(NSColor *)backgroundColor {
    return [NSColor redColor];
}

-(NSColor *)textColor {
    return [NSColor yellowColor];
}

@end

Step 2:

Drag an NSTextField to a window in Interface Builder, select the Identity tab in the inspector and select the class MyTextFieldSubclass

Step 3:

Save the IB file, build and run the application

Problem

When I run the build, the text field does not reflect the color subclassing. However, I know the subclass is being called because if I add the following method, it gets called on text changes.

-(void)textDidChange:(NSNotification *)notification {
    NSLog(@"My text changed");
}

So why does the color change not occur on the text fields?

I know that I can set the color in IB, but for anyone who has dealt with a lot of UI elements that all need the same styling, subclassing makes life way, way easier.

Ironically, I have never had to subclass an NSTextField before and this one has me stumped.

As usual, any and all help very much appreciated. I'm sure it will turn out to be a "Doh!" moment - just cant see the wood for the trees right now (plus I'm exhausted from watching too much World Cup Football early in the morning which never helps).

=== SOLUTION ===

As offered by Jaanus the solution is to put it into the viewWillDraw method. Thus my (simplified) method would now look like this:

@implementation MyTextFieldSubclass

-(void)viewWillDraw {
    [super setBackgroundColor:[NSColor redColor]];
    [super setTextColor:[NSColor yellowColor]];
}

@end

Thanks guys for your help.

+1  A: 

Since you can set the background color and text color of an NSTextField in the Attributes inspector of Interface Builder, or you can use -setBackgroundColor: and -setTextColor: programmatically, there should be no need to create a subclass just to change colors. Seems like overkill.

Edit: If styling a lot of elements is a problem, have you considered using bindings for the colors? Last I checked, NSTextField doesn't have a binding for background color, but since you're using a subclass anyway, you can add a binding for it.

Preston
Thanks Preston but there is a need to subclass. Setting the attributes in IB means you have to do it to every field. In addition allowing the user to dynamically change the settings becomes difficult to manage when set directly in IB. In addition doing a `-setTextColor` and background on each view means managing a whole host of settings. Subclassing eliminates all of these problems. In addition allows for updating in the future in one location without having to touch many files. This is the ideal use of subclassing.
Hooligancat
Have you considered using bindings for the colors?
Preston
To be honest no, but that could be an option. Generally speaking I try to avoid using bindings from IB because it makes life complicated when tracking code changes between developers using source control software. I guess in theory I could bind the colors, but would I not still need to subclass or define the bindings on every textfield?
Hooligancat
Preston - Jaanus came up with a working solution (I'll update my post above to reflect the change). Thanks for your feedback. I'll reserve the binding strategy until another day! :-)
Hooligancat
Glad you got it solved!
Preston
+1  A: 

Preston is correct: to change the colors, you should not subclass, just change textfield properties. (Oh it's NSTextfield, it probably does not have properties then... well, just use the getter and setter methods or configure it correctly in IB.)

As to

So why does the color change not occur on the text fields?

Because you are confusing getters and setters. In your subclass, you have implemented them as getters, where they just return a color. In reality, they should be setter functions (and this is how they are implemented in the guts of NSTextfield): you pass them a color, and they then go and fiddle with whatever internals NSTextfield has, to make the color change happen.

EDIT: ok, if you are subclassing because you always want to set a specific color, you would do something like

-(void)viewDidAppear { // or whatever is the Appkit equivalent
    [super setBackgroundColor:...];
}
Jaanus
Jaanus, I don't think Preston's answer is right for managing a lot of text fields in a big application (for the reasons I specified in my comments to his answer). I agree it works and is one way of doing it, but it doesn't resolve the issue of managing a lot of text fields that all need exactly the same styling. As I understand it the subclass I have in the example above should return "red" for the background and "yellow" for the text on any TextField that uses this subclass. I have no need to call a setter when I can force the color to be returned in the getter.
Hooligancat
I think my last edit is what you are looking for.
Jaanus
Jaanus - your edit crossed in the messages! You got it. I put it in the `-(void)viewWillDraw` method. Worked perfectly. Thank you.
Hooligancat