oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Working with Tables: Writing an Address Book Application
Pages: 1, 2, 3, 4

Setting up a table

Learning CocoaLearning Cocoa
By Apple Computer, Inc.
Table of Contents
Sample Chapter
Full Description
Read Online -- Safari

To configure our table view, open Interface Builder's Info panel. This can be found under the Tools menu with Show Info, or by hitting shift-command-i. Select the table view to display its attributes in the Info panel; if the attributes are not shown, select "Attributes" from the pop-up menu at the top of the Info panel.

What we want to do first is change the number of columns. In the # Colms: field enter "4", one for each of the fields we've decided to have in our address book. If you can't see all of the columns after changing the number of columns, you might need to resize them so more can fit in the view. Double-clicking on the table view will select it in such a way that allows us to change the size of the columns by dragging the dividing lines between the column headers. When you select a table view by double-clicking there will be a heavy outline around the table view, and it will also be shaded.

While we're still editing the table view's attributes, make sure that in the "Selection" box the options "Allows Empty Selection", "Allows Multiple Selection", and "Allows Column Selection" are all checked.

Next on the docket for our table is to name our columns. After double-clicking on the table (like we just did to resize the columns), you will be able to select individual columns by clicking on each column's header. When you do this, the Info panel will change to show attributes for the selected column (which is an instance of NSTableColumn), rather than table as a whole (NSTableView). Here we must do two things: set the name that will appear in the column header, and give each column a unique identifier. The column identifier is a string that we will use in the code to determine which column is which. Now set up all of your columns.

I gave my columns the same names as the text field labels in the interface -- "First Name", "Last Name", "Email", and "Home Phone" -- and made the identifiers for each column the same. In NSTableColumn info, you can also set how you want text to be aligned in the column headers, and in the data cells themselves. Now would be a good time to save your work and double check that we set all of the column identifiers correctly. It's also not such a bad idea to really be careful and double-check your work in Interface Builder; this is probably one of the last places you'll look for errors when your application isn't acting behaving properly (for me, anyway).

Before we close up shop in Interface Builder, we've got one last very important thing to do. For the table view to work properly, it has to be able to go somewhere to get the data that its going to display -- a data source object. Interface Builder has provisions for connecting a table view to a data source object in the same way we wired the interface to actions and outlets.

In this application, we'll make Controller the data source. To make this connection, first make sure the table view is double-click selected, and then drag a wire from the table view object to the Controller object, and in the Connections panel highlight "dataSource" in the list of outlets (yes, it's just an outlet, nothing too fancy), and make the connection by clicking on the Connect button.

Save your work again in Interface Builder, and we'll head back to Project Builder to lay down our code.

Coding the address book

Coding AddressBook involves three pieces. The first thing we need to do is set up some sort of data structure to store our address book entries. Then we'll implement our action methods, and finally we will enable Controller to work as a proper table view data source. Let's start with a discussion about how we'll store data, and how to set this up in code.

Dictionaries and arrays

In constructing a data structure for this application, we have to consider that we have two levels of data storage. On one level, we have to deal with the individual entries (the records) that store a person's first and last name, their email address, home phone number, and anything else we may decide to store in the future. On another level, we have to collect all of the individual records and store them in an efficient and convenient manner.

One way we could organize the data of a record is with an array where each index corresponds to a different field -- index 0 = first name, index 1= last name, and so forth. However, this can be cumbersome because we can't directly refer to the record fields by name. If we wanted a piece of information from a record, we would have to first figure out what index we need to recall, and then get the data at that index. A better way of storing this information would be to just tell the record to return the "First Name", or "Home Phone", or whatever. It just so happens that we have a data structure that will do this for us: Cocoa's NSDictionary class.

A dictionary stores data as an unordered collection of key-value pairs. What this means is that each piece of data in our dictionary has a key (some unique identifier) that we use to retrieve the associated data value. For example, in a real, book-like dictionary, the keys are the words in bold that we look up, and the associated value is the grammatical information and definition for that word. We find the definition of a word by looking up its key, and then looking at the definition. A phone book is another example of this. The name of a person is the key, and the phone number and address is the value.

A data dictionary works in the same way, except we don't have to flip through pages and pages of irrelevant information to find what we're looking for, which is decidedly inefficient. Data dictionaries handle that sort of thing much better. One of the powerful things about NSDictionary is that the key is typed to id, which means we can make the key any type of object that we want. The same goes for the stored value-type id; we can store anything in a dictionary that is an object. This opens up the way for extremely clever and powerful solutions to problems. AddressBook will simply use strings, but more esoteric applications might use any sort of custom object.

The upshot of this is that we will use instances of NSDictionary to represent each record and store an individual's information. The table below shows which keys our dictionary will have, as well as some hypothetical data values:

Key Value
First Name Mike
Last Name Beam
email Address
Home Phone (512) 123-4567

Note that the keys are all the same as the column identifiers we set above. This will prove to be most convenient and necessary when it comes to coding the data source side of Controller.

The second level of data organization is the whole collection of records. The way we're going to go this is with an array, specifically Cocoa's NSArray class. NSArray is conceptually no different than any other array you may have encountered in programming. An NSArray is just a collection of ordered, indexed elements. The only thing that is different is the interface for adding, inserting, deleting, and otherwise accessing members of an array collection.

Like dictionaries, Cocoa arrays give us the ability to transparently collect and organize any type of object we want. What I mean by transparently is that the interface for working with an array of text fields (we'll do this in a later column) is exactly the same as working with an array of strings, which is no different than working with an array of marines and siege tanks in some Starcraft-esque game. It might be overkill to suggest that dictionaries are valid array citizens too.

The plan then, is to collect all of our records into an array. Every time we create a dictionary, we'll store it in our array. Every time we need to get data out of a dictionary, we'll first retrieve the dictionary from the array.

Because most of our methods will use this array, we need to provide a global access point to it . This is done easily enough by declaring a new instance variable in Controller.h. We're using a mutable array (NSMutableArray) as opposed to a static array (NSArray) for the obvious reason that we want to be able to add, insert, or remove records at any point during runtime. NSMutableArray is just like NSArray, except you can change its contents after it's created.

Add the instance variable declaration for an NSMutableArray object named "records" to Controller.h in the same code block as where the outlets are declared:

NSMutableArray *records;

Before any of our operations can use the records array, we need to initialize it somehow when the application launches. The awakeFromNib method is a good place to do this. As an application is loading and unpacking its nib file, each object contained in the nib file is sent an awakeFromNib message (but not every class necessarily implements this message). We're going to set up Controller to initialize a new, empty records array when awakeFromNib is invoked.

Going back to Controller.m, add the following method

	records = [[NSMutableArray alloc] init];

Another way you can initialize this array is by overriding -(id)init. Both will serve to have everything ready when we need it. The first piece of our code is now in place, and we're ready to move onto implementing the action methods we declared in Interface Builder.

Pages: 1, 2, 3, 4

Next Pagearrow