macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

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

Second, the user defaults

This section concerns itself with using the foundation we have just built to initialize the table to display those columns that the user wants according to their preferences in the defaults database. The state which our application ended up in at the end of the previous column already had provisions for accessing users defaults through the NSUserDefaults instance prefs.

A simple way of storing which columns a user wants in their table is an array of column identifiers. Thus, we would have a key in the user's defaults database named "User Columns" and that key would correspond to a preference object that is an array of strings representing the column identifiers of the user's table columns. Let's make the variable that points to this array global -- declare it in the interface file with the other instance variables (you will see in a bit why we're making userColumns mutable):

NSMutableArray *userColumns;

The code in awakeFromNib to initialize userColumns looks like the following:

if ( [prefs arrayForKey:@"User Columns"] != nil ) {
  userColumns = [[NSMutableArray alloc] initWithArray:[prefs arrayForKey:@"User Columns"]];
  [self initializeTableWithColumns:userColumns];
} else {
  userColumns = [[NSMutableArray alloc] init];
}

The important thing to notice here is the if statement. What we're doing is first checking to see if an object exists for the key @"User Columns". If so, then we initialize userColumns to the array stored in user defaults, and invoke a new method initializeTableWithColumns: because, presumably, a user's preferred columns are different from the set we established in Interface Builder. Otherwise we just initialize userColumns to an empty mutable array, ready for use. Put another way, the table we set up in Interface Builder is the default table configuration, and it only gets changed if there is a user preference indicating so.

The second line calls a new method that we have yet to define called initializeTableWithColumns:, which takes an argument that is the array of identifiers from preferences for the columns that are to appear in the table. The meat of this step is contained in this method, so let's see what we can do.

The class NSTableView defines several methods that allow us to add, remove, and otherwise manipulate columns in the table. For example, we will use the method addTableColumn: to add a column to the table, and removeTableColumn to remove a column from the table. Both of these methods take an NSTableColumn: as their argument.

The most straightforward thing to do would be to remove the existing table columns from the table that we created in Interface Builder, and then add the columns indicated by the user preferences. We'll do this using two different enumerators, as follows:

- (void)initializeTableWithColumns:(NSArray *)identifiers
{
  NSEnumerator *e;
  id column, identifier;

  // clear out the existing columns in tableView
  e = [[tableView tableColumns] objectEnumerator];
  while ( (column = [e nextObject]) ) {
    [tableView removeTableColumn:column];
  }

  // add columns from the argument array
  e = [identifiers objectEnumerator];
  while ( (identifier = [e nextObject]) ) {
    column = [tableColumns objectForKey:identifier];
    [tableView addTableColumn:column];
  }
}

You can see how this method uses two enumerators. The first enumerator is for the array of table columns currently displayed in tableView. This array is obtained by sending tableView a tableColumns message, which returns the array of all NSTableColumn objects contained in the table. We immediately create an enumerator by sending an objectEnumerator message to this array, and proceed with our first while loop. This while loop simply enumerates the array of existing NSTableColumns, and removes them one by one, via the line [tableView removeTableColumn:column].

With that we have a table devoid of columns. Next we create a new enumerator, this time one to enumerate the argument array which contains the identifiers of the columns we want to add to the table view. In the while loop we get from tableColumns the actual NSTableColumn object whose key matches identifier, and add each NSTableColumn to tableView. And that's all there is! We've configured the table view to contain only those columns that the user wants.

A random aside

As a random aside, if you were in the business of making applications with multiple table views, you could modify initializeTableWithColumns: to work with multiple tables. You would have to change the name to initializeTable:withColumns: to add another argument, which is a table view. The actual implementation would change little:

- (void)initializeTable:(NSTableView *)aTableView withColumns:(NSArray *)identifiers
{
  NSEnumerator *e;
  id column, identifier;

  // clear out the existing columns in tableView
  e = [[aTableView tableColumns] objectEnumerator];
  while ( (column = [e nextObject]) ) {
    [aTableView removeTableColumn:column];
  }

  // add columns from the argument array
  e = [identifiers objectEnumerator];
  while ( (identifier = [e nextObject]) ) {
    column = [tableColumns objectForKey:identifier];
    [aTableView addTableColumn:column];
  }
}

Notice that all we did was replace all instances of tableView, our outlet, with the argument variable name aTableView. In awakeFromNib where we call this, we would have the line:

[self initializeTable:tableView withColumns:userColumns];

You can use this version if you like for this application, but it doesn't really matter as we have only one table.

Saving table column preferences

The next step in dealing with table column preferences is to implement a method to save the current table configuration to user defaults. We'll call this method saveTableColumnPrefs. This method is analogous in function to the method saveData that we implemented two columns ago. The implementation shown here will be very straightforward, saving only information about which columns were in the table and in what order they appeared. Later on we'll discuss possibilities for saving more information about the table configuration.

In this method we will create an enumerator of the table columns like we did above, clear out the array userTableColumns, and then repopulate userTableColumns with the identifiers of the columns currently contained in the table. So let's see this in code:

- (void)saveTableColumnPrefs
{
  id column;
  NSEnumerator *e = [[tableView tableColumns] objectEnumerator];
  
  [userColumns removeAllObjects];

  while ( (column = [e nextObject]) ) {
    [userColumns addObject:[column identifier]];
  }
  
  [prefs setObject:userColumns forKey:@"User Columns"];
}

Here is what we have: First we create the enumerator from the array of table columns returned by the tableColumns message to tableView. The next line clears out the userColumns array using removeAllObjects. Now we go into the while statement using the enumerator to get the next column in the array. Within the loop, we add to userColumns the string returned by an identifier message to column. At the end of this loop, we have an array of identifiers that preserves the order of table columns in the table, but before we leave the method in the last line, we tell prefs to make userColumns the object associated with the @"User Columns" key. This method will be used in a moment by the setColumn action method.

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

Next Pagearrow