André's Blog

PHP, Zend Framework, Mac/Cocoa Programming

Phonebook/NSTableView Tutorial for Dummies(Xcode 3.1.3)

with 15 comments

Introduction

This tutorial explains how to create a simple phonebook with a search function which will look similar to this:

Phonebook Tut Step 1

The following things will be covered by this tutorial:

  • creating a window with a drawer
  • feeding a NSTableView with an NSArrayController
  • search in the table using NSPredicate to filter the array

Getting Started

Creating the Interface

Start by – this should come as no surprise to you – creating a new Cocoa project and open the MainMenu.xib in IB. Remove the Window from the Document Window and instead drag in a “Window and Drawer” (Library->Cocoa->Application->Windows).

Click the new window twice to open it and drag a Toolbar(Library->Cocoa->Application->Toolbars) onto it.  In Library->Cocoa->Views & Cells->Inputs & Values which you can add to the toolbar(beforehand you need to click on the toolbar twice to open the configurator).

Remove everything unnecessary from the toolbar and add a “Image Toolbar Item”. When you select this item you can change its image in the attributes tab. Set it to “NSAddTemplate”. There you can also change the label.

Phonebook Tut Step 2

Next, add a NSTableView to the window so that it fills the remaining space. Select the ScrollView surrounding the NSTableView and go to the Size tab where you should enable autosizing by clicking on the inner double arrows in the rectangle:

Phonebook Tut Step 3

Now select the TableView and click on the Attributes tab here you can change the number of columns to 3. Resize the columns so that they look okay and rename them(click ‘em twice) to “First Name”, “Last Name”, “Phone Number”.

Click twice on the “Drawer Content View” in the Document Window so that it opens and add some labels and textfields so that it looks like this(altough I encourage you to make it look better):

Phonebook Tut Step 4

Adding Outlets/Actions

Add an ArrayController(Library->Cocoa->Objects & Controllers->Controllers) and an Object(same place) to the Document Window. Change the “Class Name” of the Object to “WindowController”(Identity tab) in the same tab add the following things:

Phonebook Tut Step 5

  • 2 Actions:
    • addContactAction
    • updateFilterAction
  • 6 Outlets:
    • arrayContacts(NSArrayController)
    • txtSearchField(NSSearchField)
    • tblContacts(NSTableView)
    • txtFirstName(NSTextField)
    • txtLastName(NSTextField)
    • txtPhoneNumber(NSTextField)

Finally go to File->Write Class Files.. while having the WindowController selected.

Click the toolbar twice, so that the configuration comes up and link the Add Contact button to the Drawer’s(in the Document Window) toggle action, so that it toggles the drawer’s state between in and out every time the button is clicked.

Now connect every Class Outlet and every Class Action that you created before(if you don’t know how to do that check out my other tutorials).

The Actual Implementation

Adding a property

Now we need to add a property for our contacts(If you want to learn more about properties see chapter 9 on this page). To add it open up WindowController.h and add a NSMutableArray contacts and a associated property “contacts”. Also specify the superclass of the WindowController which we choose to be NSWindowController:

@interface WindowController : NSWindowController {

    IBOutlet NSArrayController *arrayContacts;
    IBOutlet NSTableView *tblContacts;
    IBOutlet NSTextField *txtFirstName;
    IBOutlet NSTextField *txtLastName;
    IBOutlet NSTextField *txtPhoneNumber;
    IBOutlet NSSearchField *txtSearchField;

    NSMutableArray* contacts;
}

- (IBAction)addContactAction:(id)sender;
- (IBAction)updateFilterAction:(id)sender;

@property (copy) NSArray* contacts;

@end

In the WindowController.m add a corresponding synthesize statement:

...

@implementation WindowController

@synthesize contacts;

...

Loading the Contacts from a file

Whenever you run the application we want to the contacts to be read from a .plist and added to the table, so let’s add a awakeFromNib to the WindowController.m which is called when the interface finished loading.

...
@implementation WindowController

@synthesize contacts;

NSString *filePath;

...

- (void)awakeFromNib {
	//set up the path of the plist(which will reside in the Resource folder of our app
	filePath	= [[[NSBundle mainBundle] pathForResource:@"Contacts" ofType:@"plist"] retain];

	//initialize the contacts array
	contacts  = [[NSMutableArray alloc] init];

	//check if the file Contacts.plist exists
	if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {

		//if it does read its contents into an array and add the contacts to the arrayController wich will then feed the table
		NSMutableArray *contactsFile = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
		for (id contact in contactsFile) {
			[arrayContacts addObject:contact];
		}
	}
}
...

Connecting the ArrayController to the TableView

Now we need to connect the ArrayController to the property we created, so open up Interface Builder and go to the Bindings tab while having the ArrayController selected. There go to “Content Array” and bind it to the Window Controller. As the “Model Key Path” choose “contacts”(the name of our property).

Keep the Bindings tab open and select the “First Name” column of the table, go to “Value” and bind it to the arrayController use “arrangedObjects” as the Controller Key and “firstName” as the Model Key Path.

Phonebook Tut Step 6

Repeat this procedure for the other two columns and choose “lastName” and “phoneNumber” as Model Key Path.

Now whenever a new entry is added to the arrayController the table is filled with the informations you just binded to it.

The First Contact

You could run this to see if it works if you already had a Contacts.plist. If you want to try it, copy this to “build/Debug/Phonebook.app/Contents/Resources/Contacts.plist”:

< ?xml version="1.0" encoding="UTF-8"?>
< !DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<dict>
		<key>firstName</key>
		<string>John</string>
		<key>lastName</key>
		<string>Doe</string>
		<key>phoneNumber</key>
		<string>0123456789</string>
	</dict>
</array>
</plist>

Note that this is not necessary and only serves the purpose of having some sense of achievement.

Adding Contacts

To be able to add new contacts all we have to do is to fill the body of the addContactAction:

- (IBAction)addContactAction:(id)sender {
	//create a new contact based on the input values
	NSDictionary *dict =[NSDictionary dictionaryWithObjectsAndKeys:
						 [txtFirstName stringValue], @"firstName",
						 [txtLastName stringValue], @"lastName",
						 [txtPhoneNumber stringValue], @"phoneNumber",
						 nil];

	//add it to the arrayController
	[arrayContacts addObject:dict];

	//save the changes to the plist
	if ( ![[NSFileManager defaultManager] fileExistsAtPath:filePath] || [[NSFileManager defaultManager] isWritableFileAtPath:filePath]) {
		[[arrayContacts arrangedObjects] writeToFile:filePath atomically:YES];
	}
}

Try it out! Also try to restart the application to see if your changes have been saved.

Making the Seach work

We’ll be using predicates to filter the TableView. To learn more about the NSPredicate class see the Mac OS X  Reference Library.  It’s pretty similar to SQL if you’ve been using that before.

Here is how it works: First we create a “template” which contains the query but no actual values. We’ll do that in the awakeFromNib method:

...
@implementation WindowController
...
NSPredicate *predicateTemplate;
...

- (void)awakeFromNib {
	//creating a predicate with the condition that either firstName, lastName or phoneNumber contains the searchString (case- and diacritic-insensitive)
	predicateTemplate = [[NSPredicate predicateWithFormat:@"(firstName contains[cd] $searchString) or (lastName contains[cd] $searchString) or (phoneNumber contains[cd] $searchString)"] retain];
...

Whenever the value in the seach field changes the updateFilterAction is called, so let’s put our search logic there:

- (IBAction)updateFilterAction:(id)sender {

	NSString *searchString = [txtSearchField stringValue];
	NSPredicate *predicate;

	//if the search field is empty do not search!
	if ([searchString isEqualToString:@""]) {
		predicate = nil;
	} else {
		//create a new dictionary with the search string
		NSMutableDictionary *bindVariables = [[NSMutableDictionary alloc] init];
		[bindVariables setObject:searchString forKey:@"searchString"];

		//and create a predicate from the template by replacing the variable with its actual value
		predicate = [predicateTemplate predicateWithSubstitutionVariables:bindVariables];
	}

	//apply the predicate to the array controller
	[arrayContacts setFilterPredicate:predicate];
}

That’s all that needed to be done to make the search field work. Check it out!

The Source

The final Xcode project can be download from here.

Have fun!

About these ads

Written by andrehoffmann

September 3, 2009 at 3:02 pm

15 Responses

Subscribe to comments with RSS.

  1. [...] posted here: Phonebook/NSTableView Tutorial for Dummies(Xcode 3.1.3) « André's Blog Share and [...]

  2. Cracking tutorial mate, I found it really helpful.

    Cheers!

    ves

    October 18, 2009 at 8:43 pm

  3. Verry nice tut! thx..

    Sampie

    November 6, 2009 at 4:26 pm

  4. [...] een plist schrijven Hallo, ik ben een simpel programma aan het schrijven naar aanleiding van deze tutorial. De gebruiker ziet een lijstje, kan daar een muziekbestand aan toevoegen en afspelen, en [...]

  5. Why the app does not save after restart? Thats´s not good. Also I can not delete a entry if it is wrong. That´s also not good.
    Please answere me per mail: mauricio@web.de

    Sorry for my bad english.

    Mauricio Fritz

    January 25, 2010 at 1:59 pm

  6. Had anyone solved the problem with new rows that can’t be edited (saved) until the app is restarted or new row added?
    Thank you.

    mira

    January 25, 2010 at 2:47 pm

  7. Got it! Replace “NSDictionary” to “NSMutableDictionary”.

    mira

    January 26, 2010 at 12:38 am

  8. Thanks!
    it helpful!

    sakrist

    March 5, 2010 at 12:46 pm

  9. *bookmarked* good and easy reference for obj-c beginner if you want to use the table view

    Robin

    October 7, 2010 at 3:29 pm

  10. Phonebook/NSTableView Tutorial for Dummies(Xcode 3.1.3) André's Blog

    seo copywriters

    January 25, 2011 at 11:16 pm

  11. Good tutorial! really helpfull
    But I don’t understand where is the Contacts.plist file… I download ur code and try to do it myself :
    yours does save changes but mine don’t….
    can u help me? and tell me more about saving information (maybe other ways, make the program reads a .txt maybe….)

    Carmi

    February 7, 2011 at 12:55 am

  12. Thanks i keep my job for this tutorial

    Javier Beltrán

    June 14, 2011 at 11:34 pm

  13. Thannnnnkssssssssss!!!!

    Enrico

    August 1, 2011 at 12:49 pm

  14. One of your sections is titled “Making the Seach Work”. Other than that, thanks for the tutorial!

    countminus1

    May 5, 2013 at 7:26 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: