oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Adding a Preferences Window to Your Application
Pages: 1, 2, 3, 4, 5, 6, 7

First, a collection of prebuilt table columns

We stated above in our strategy that the application should create a set of prebuilt NSTableColumn instances. So how do we go about this and more importantly how do we store this collection of table columns? Ideally we would want to access prebuilt columns by their identifier, so an NSDictionary would suit us well.

In practice, when the application needs a table column, it will pass the dictionary of table columns a key which is the identifier of the column we need, and the collection will then return the matching NSTableColumn object. So let's take a shot at creating new columns and populating a dictionary with several of them when the application launches (and it needs to be one of the first things done, as the initial configuration of the table will depend on being able to access these table column objects). Before we start, declare in Controller.h the NSMutableDictionary instance variable tableColumns:

NSMutableDictionary *tableColumns;

And we initialize this instance variable in awakeFromNib in the standard fashion:

tableColumns = [[NSMutableDictionary alloc] init];

Now, what follows is the code to create one new instance of NSTableColumn, configure it, and add it to tableColumns; we'll create the "First Name" NSTableColumn.

NSTableColumn *newColumn;

newColumn = [[[NSTableColumn alloc] initWithIdentifier:@"First Name"] autorelease];
[[newColumn headerCell] setStringValue:@"First Name"];
[[newColumn headerCell] setAlignment:NSCenterTextAlignment];
[newColumn setEditable:YES];
[tableColumns setObject:newColumn forKey:@"First Name"];

First we declared a variable to temporarily store our NSTableColumn, newColumn. In the next line of code, we simply created a new NSTableColumn instance using alloc, and then initialized it using NSTableColumn's initializer method initWithIdentifier:. The argument to this method is of course the identifier we want our table column to have, which we had previously set from within Interface Builder. By autoreleasing, we're saying that we don't want to be responsible for the new table column object any longer than we need to be -- we'll let the dictionary tableColumns be responsible for all of the table columns when we add them to the collection (remember, collections sent newly added member objects retain messages, thereby asserting ownership over the member object).

The next line of code sets the title of the table column, which as you can see is the same as the identifier. Notice the way that line of code is set up. We first send a headerCell message to newColumn, which returns the object that represents the header of the column. We can't tell tableColumn to make its title such-and-such, because that's not the job of NSTableColumn. NSTableColumn relies on instances of NSTableHeaderCell to take care of that kind of stuff. The method used here, setStringValue: is declared in the class NSCell, from which NSTableHeaderCell inherits.

Following that line, we set the alignment of the title in the column's header cell. There are five possibilities for aligning text in a cell represented by five constants -- one of which you see in the code above. These five possibilities and their corresponding constants are as follows:

Alignment Mode Constant
Right NSRightTextAlignment
Left NSLeftTextAlignment
Center NSCenterTextAlignment
Justified NSJustifiedTextAlignment
The Default NSNaturalTextAlignment

As you can see above I've decided to align my column titles so that they are centered.

In the next line of code we go back to modifying the table column itself by setting whether or not it is editable, which refers to whether or not we can modify the data from within the column itself. Finally, to finish things off, we add our configured column to the tableColumns dictionary, with the column identifier as the key. Now whenever we send an objectForKey:@"First Name" message to tableColumns this column we created will be returned.

If you wish to configure your columns further, then you can put any additional configuration code between the initialization line and the line where we add it to the dictionary. For all of the possibilities, as always, check out the class documentation for NSTableColumn and NSTableHeaderCell (which will lead you up the class hierarchy to their various parent classes).

Now all we have to do is cut and paste this code for each of the five remaining columns, and change the identifier string in each one. Hah! That would be too much busy work and it is much too inflexible for our tastes, so we continue on to modify this foundation block of code.

One modification we could make would be to encase this code in a new method that takes the identifier as an argument. So rather than cutting and pasting this large block of code we could simply invoke this new method six times, each time changing the identifier argument. This method is called addInitialColumnForIdentifier: ("add..." in the sense that we are adding a new column to the tableColumns dictionary) , and would look like the following:

- (void)addInitialColumnForIdentifier:(NSString *)identifier
  NSTableColumn *newColumn;

  newColumn = [[[NSTableColumn alloc] initWithIdentifier:identifier] autorelease];
  [[newColumn headerCell] setStringValue:identifier];
  [[newColumn headerCell] setAlignment:NSCenterTextAlignment];
  [newColumn setEditable:YES];
  [tableColumns setObject:newColumn forKey:identifier];

All I did here was copy the original code into the method definition, and change all the static @"First Name" identifier strings to the variable identifier. Now in awakeFromNib when we want to fill tableColumns with our six columns, we need only invoke this method six times, changing the identifier:

[self addInitialColumnForIdentifier:@"First Name"];
[self addInitialColumnForIdentifier:@"Last Name"];
[self addInitialColumnForIdentifier:@"Email"];
[self addInitialColumnForIdentifier:@"Home Phone"];
[self addInitialColumnForIdentifier:@"Work Phone"];
[self addInitialColumnForIdentifier:@"Mobile Phone"];

So, our code is a little more appealing to the intellect (at least in my mind) and a little bit more flexible, but I contend that we can do even better. Why should we hard-code six calls to this method, when we know of a way to automatically do something like that with enumerators? Yes my friends, enumerators.

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow