macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Creating a Color Meter Using Cocoa
Pages: 1, 2

The controller object

In object-oriented programming, there are several paradigms for organizing objects and classes in an application. Our application is simple so our needs for organization are simple.



We will employ what is known as the Model-View-Controller paradigm. "View" is the interface of your application -- the sliders, text fields, and color well in our application. "Model" is the data model for our application.

Our application doesnít deal with data at all, so we wonít have a model object. "Controller" is an object that that links your interface to the data model. In this paradigm, the interface should not know anything about how the data is stored and managed, and the model should have no knowledge of how to display the interface. It's the controller objectís responsibility to communicate between these two realms. Let's make our controller object.

From the classes tabbed view, find the NSObject class in the list of available classes. We want to create a subclass of it. Do this by selecting "Subclass" from the Classes menu, and name it "Controller".

Now add the objects and actions shown in the picture below with the same names. New objects and actions can be created from the same Classes menu.

Screenshot.
Adding objects and actions.

The next step is to instantiate our new class so we can connect the outlets and actions to their appropriate interface objects. The names should explain how to wire them up. Remember, wire actions by starting at the interface object -- a slider -- and control-drag to the instance of the Controller. Objects are wired in the opposite direction. Remember, wire in the direction messages would travel.

Once you have made all the connections, you need to add the interface and implementation files of our controller class to our project in Project Builder. This is done easily enough by going back to the Classes tab, selecting the Controller class, and then selecting Create Files from the Classes menu.

Close Interface Builder and return to Project Builder.

Coding

The way our color meter application operates is as follows: The user of the application will be able to slide the color sliders about for each component of the final color: red, green, blue, and alpha. As the user manipulates the controls, the color will update in real time. Additionally, the value of the slider will be displayed in the text field to the right of each slider. Now, all we have to do is supply the code that will create a color in the color well that corresponds to the values of red, green, blue, and alpha.

The first thing we want to do is declare four instance variables of type "float" that store values of each component of the final color. In the interface file Controller.h insert after the IBOutlet instance variables the following four variables:

    float redValue;
    float greenValue;
    float blueValue;
    float alphaValue;

Additionally, we want to add a method to this class that will update the color displayed in the color well. That method is

- (void)updateColor;

So our code should look like this:

#import <Cocoa/Cocoa.h>

@interface Controller : NSObject
{
    IBOutlet id alphaField;
    IBOutlet id alphaSlider;
    IBOutlet id blueField;
    IBOutlet id blueSlider;
    IBOutlet id colorWell;
    IBOutlet id greenField;
    IBOutlet id greenSlider;
    IBOutlet id redField;
    IBOutlet id redSlider;
    float redValue;
    float greenValue;
    float blueValue;
    float alphaValue;
}
- (IBAction)setAlpha:(id)sender;
- (IBAction)setBlue:(id)sender;
- (IBAction)setGreen:(id)sender;
- (IBAction)setRed:(id)sender;
- (void)updateColor;
@end

The four action methods that set the color components will all have the same structure.

Here is the code for setBlue:

- (IBAction)setBlue:(id)sender
{
    blueValue = [sender floatValue];
    [blueField setFloatValue:blueValue];
    [blueSlider setFloatValue:blueValue];
    [self updateColor];
}

In the first line, we are asking the sender who is invoking the setBlue method for the floatValue of its data, and storing that in blueValue. In this case, the sender can be one of two objects in the interface.

If you recall, we wired both the Blue slider control and the Blue text field to the same action. Depending on the user's actions, either of these objects can be the sender of the message invoking this method. This is nature of all IBAction methods. That is, the action method argument is the sender object. This makes it easy to get information from the interface, while maintaining a good deal of flexibility in our code, as was demonstrated here. It also means we donít have to write an additional action methods for additional controls to interact with. We can add as many controls as we want to change the blue color value and wire them all to this same action. This is also the power of polymorphism, as all Cocoa controls respond to the floatValue method in the same way, by returning a float-typed number.

The purpose of the middle two lines is to synchronize the values displayed in the slider and the text field. Thus, if the slider was the sender, the text field would take on the appropriate value, and vice versa. The setFloatValue: method does the exact opposite as floatValue. Where floatValue returns the value of the control as a float, setFloatValue: takes a float argument and sets the controls data value to the argument.

The last line simply tells the controller object (self, since this method is sending a message from itself to itself) to update the color displayed in the color well. We could have put the appropriate code to update the color in the color well in each of the "set...methods", but one of the goals of object-oriented programming is to reuse as much code as possible. Thus, by extracting the update code from each method and placing it in its own method, which is called by the "set...methods", we are reusing that code. The clear benefit of this is that if we want to change that code for whatever reason, we need only do it in one place, rather than four.

The setGreen:, setRed:, and setAlpha commands are exactly the same as setBlue:, except everywhere you see Blue, you replace it with Red, Green, or Alpha; and everywhere you see blue, you replace it with red, green, or alpha.

Since updateColor is not an Interface Builder action method like the others, we have to define it manually (that's why we had to add it to the interface file earlier as well). Here is the code for the updateColor: method:

- (void)updateColor
{
   NSColor *aColor = [NSColor colorWithCalibratedRed:redValue green:greenValue blue:blueValue alpha:alphaValue];
   [colorWell setColor:aColor];
}

What we do first is create a new color by calling the colorWithCalibratedRed: green: blue: alpha: method and store the returned color object in the aColor variable, which is declared locally on the same line. The NSColor method we invoke here is one of many that are available for creating colors. There are some NSColor class methods that return a present color, such as redColor. If you want to read more about NSColor, check out its class reference page here: NSColor (Objective-C).

Finally, in the same way we display a number in a text field using setFloatValue:, we display a color in the color well object by sending it a setColor: message with the newly created color object.

The end

And thatís it! Compile your code (hopefully, you wonít have any errors), run it, and play around with it. If you want to see another cool freebie in Cocoa, click on the color well and before your very eyes watch the system color-picker appear. Now we didnít add any code to update the text fields and sliders to reflect the new color being set in the color well through the picker, but I encourage you to read the class references for the classes we talked about, and see if you canít make that work (if you get stuck, write it up as a Talk Back and we'll figure it out online).

Screen shot.
After you compile the code and run the application, you should have an interface that looks like this.

In the next few columns, Iím going to be doing less complete applications, and talk more about individual classes from the Foundation framework that deal with strings, numbers, and collections. Hope you enjoyed this one. Talk to you next time!

Michael Beam is a software engineer in the energy industry specializing in seismic application development on Linux with C++ and Qt. He lives in Houston, Texas with his wife and son.


Read more Programming With Cocoa columns.

Return to the Mac DevCenter.