Dealing with the triangles in a NSOutlineView

Monday, April 03, 2006

Since the solutions for "Cocoa(R) Programming for Mac(R) OS X (2nd Edition)" don't seem to generate that much interest and they take a pretty good amount of time to make a decent blog entry, I'm taking a little different direction on this blog. I'm going to post small snippets of code that I've found rather tricky to develop.

The first of which is a quick way to hide the little triangles in an outline view. I've searched many sites for an answer to this question and didn't really get an example of doing this. So here it is.

The first thing to make you aware of is that a NSOutlineView has a delegate method which allows you to override the display of the outline column's cell.

Here's the method...
- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item

Approach 1

Just replace the triangle images with other images. (This requires the image to be the same size as the triangle)
- (void)outlineView:(NSOutlineView *)theOutlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if (item) {
[cell setImage:[NSImage imageNamed:@"down"]];
[cell setAlternateImage:[NSImage imageNamed:@"up"]];

Approach 2

First, hide the triangle completely.

- (void)outlineView:(NSOutlineView *)theOutlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if (item) {
[cell setTransparent:YES];

Now we use the non-outline delegate method to set up a button cell to do the expand and collapse for the row.
- (void)outlineView:(NSOutlineView *)theOutlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item {
if ([[tableColumn identifier] isEqualToString:@"outline"]) { //use the appropriate identifier for you column
if ([self childCountOfItem:item] > 0) {
if ([theOutlineView isItemExpanded:item]) {
[cell setImage:[NSImage imageNamed:@"up"]];
} else {
[cell setImage:[NSImage imageNamed:@"down"]];

//set up an action to emulate the clicking you would normally do on the triangle

[cell setAction:@selector(toggleItem:)];
[cell setTarget:self];
} else {
[cell setImage:[NSImage imageNamed:@"unexpandable"]];

- (void)toggleItem:(id)sender {
id item = [outlineView itemAtRow:[outlineView selectedRow]];
if ([outlineView isItemExpanded:item]) {
[outlineView collapseItem:item];
} else {
[outlineView expandItem:item];

As always feedback is encouraged...

FreeMacWare used for development

Tuesday, March 21, 2006 contains reviews of applications that many a mac user could find useful. For me, the most useful are the ones surrounding software development.

This software allows me to just drag a folder, or a release version of the tool/application I just compiled and provide it as a disk image. Extremely useful!

A easy to run wiki for developers to share information.

Allows you to take an iphoto album and quickly generate a small website to display all the photos. Very useful for UI prototype screens. I've also used it to display pictures of white boards with design drawings.

Open Terminal Here
Because sometimes you just can't live without a command-line. This opens a terminal window from a folder location in the finder.

Chicken of the VNC
I use a 15" powerbook for all my demonstrations to the powers that be. I use Chicken of the VNC allows me to show other machines (Windows/Mac/Linux) on my powerbook without needing to have multitude of machines in the room.

Of course I also have the FreeMacWare widget in my dashboard and I often find myself looking forward to each new entry!

IBPalette Chapter Update

Thursday, February 02, 2006

Just to let you know...

The Big Nerd Ranch has posted an update chapter to replace Chapter 27 : Creating Interface Builder Palettes.

XCode 2.1 changed the Project Template for creating IB Palettes. The newly posted chapter explains how to create both a framework and an IBPalette in this updated chapter.

Click here to view the new chapter here

Chp 6 Challenge 2 Solution

Monday, November 14, 2005

So the solution to this challenge involves a lot of editing within the MyDocument.nib file, but first we'll set up a new project so we won't ruin the one that uses the NSArrayController.

1. Create New Cocoa Document Based Project
2. Add the Person.h and Person.m from earlier in the chapter to the project.
3. Use the code shown for the MyDocument class.
4. Open the MyDocument.nib.
5. Layout the NSTableView and the two buttons just as you did in the earlier project. Make sure to set up the formatter on the second column.
6. Select the first NSTableColumn of the table. Set its identifier attribute to "personName".
7. Select the second NSTableColumn of the table. Set its identifier attribute to "expectedRaise".
8. Go back to the XCode project and drag the MyDocument.h file to the nib window to update the MyDocument class.
9. Now connect the FileOwner (aka the MyDocument instance) to the NSTableView and make it the tableView outlet.
10. Connect the Create New button to the FileOwner's createEmployee action.
11. Connect the Delete button to the File Owner's deleteSelectedEmployees action.
12. Connect the NSTableView to the File Owner and select the NSTableView's dataSource delegate.

That's enough to build and run the application to seed employees into the table.

To add sorting you need to add the following method to the MyDocument.m file....

- (void)tableView:(NSTableView *)aTableView
sortDescriptorsDidChange:(NSArray *)oldDescriptors {
NSArray *newDescriptors = [aTableView sortDescriptors];
[employees sortUsingDescriptors:newDescriptors];
[aTableView reloadData];

Once you've added that method go back to the MyDocument.nib file do the following steps...
1. Select the first NSTableColumn and make the sortKey "personName", and the selector "caseInsensitiveCompare:"
2. Select the second NSTableColumn and make the sort "expectedRaise", and leave the selector as "compare:"
3. Connect the NSTableView to the File Owner object and make the File Owner the delegate object for the NSTableView

Build and run to see the sorting work.

Click here to download a working version of the project. This project was created using XCode 2.2 and will not immediately work with previous versions of XCode.

Chp 6 Challenge 1 Solution

Monday, November 07, 2005

This one is really quite simple. As the hint shows, there is a length method that you can use. As you have read about KeyPaths, one page before the Challenge 1 is made key paths are accessed using a period. So to answer this challenge after you've completed the excercises, go to the MyDocument.nib in Interface Builder.

Select the first column of your table, then go to the attributes of the column to view the the sort key field. Enter "personName.length" as the sort key. Now set the Sort Selector to "compare:". Save the nib file, compile the project and run.

Now clicking the column header for person name will sort based on the length of the person's name, rather than alphabetical order.

Chp 5 Challenge Solution

Thursday, October 06, 2005

In continuation of the sharing of my solutions for challenges shown in Cocoa Programming for Mac OS X, here's chapter 5. The challenge is to create an application to demonstrate the use of a delegate for a NSWindow that keeps the height at twice the desired width.

Start out by creating a new Cocoa Application project.

Once ready open the MainMenu.nib file from the project in Interface Builder. In the classes tab of the MainMenu.nib palette, create a subclass of an NSObject and name it "WindowResizer". Now create an instance of the WindowResizer class. Once that's done, switch to the Instances tab, and control drag from the Window to the WindowResizer instance, and connect the WindowResizer object as the delegate of the window.

Now we need to create the files for the WindowResizer class. To do so, go back to the classes tab and select the WindowResizer class we've added. Now create the .m and .h files adding them to your project. (Classes menu option)

Save and close the MainMenu.nib file.

Now we need to go to XCode and edit the WindowResizer.m file. Add the following method to the implementation block.

- (NSSize)windowWillResize:(NSWindow*)sender
toSize:(NSSize)frameSize {
return NSMakeSize(frameSize.width, frameSize.width * 2);

Compile and run the application and you should see the window resize with the contraint we added. One thing you'll probably notice is that the first time we resize the window shifts abruptly, because the starting dimensions don't jive with the constraint so the first time the delegate message is sent, the window will shift quite a bit. To lessen the shock you could go back to the nib file and have the window start with a width of 250 and a height of 500, or some other dimension that match the constraint.

Happy Cocoa Travels!

Customizing XCode File Templates

Sunday, September 18, 2005

Want to customize/organize your XCode 2.1 templates??

Here's a few things I do to help myself.

First off let's get an understanding how this stuff works.

The location of file templates used for XCode are stored in /Library/Application Support/Apple/Developer Tools/File Templates/

You'll find several folders in the file templates folder. Notice that the names of the folders coincide with the names of the headings in the sheet that appears when you add a new file to your project.

Also, within each of those folders are folders with names that end in .pbfiletemplate. (The 'pb' is a left over from the previously used Project Builder application.) Within each .pbfiletemplate folder there is a class.h, class.m template file, and a TemplateInfo.plist. Just as an example open the /Library/Application Support/Apple/Developer Tools/File Templates/Cocoa/Objective-C class.pbfiletemplate folder. In that folder you'll find a class.m, class.h, and a TemplateInfo.plist file.

Open the TemplateInfo.plist file, which should automatically open in the Property List Editor application. In the Root node you'll find the Description of the template you see when you select this file template in the New File dialog. You'll also see the MainTemplateFile and its CounterpartTemplateFile which are set to class.m, and class.h respectively. Close the file without saving any changes you may have made.

Now open the class.m file. You'll see the template of the standard .m file. Within this file you'll see several elements using a particular pattern like the one for the file name ¬'FILENAME¬". These elements are replaced when you create an instance of the file in your project. Most of these elements are one's you probably don't want to mess with. However, one such element is listed as ¬'ORGANIZATIONNAME¬" which normally defaults to "__MyCompanyName__". This particular listing can be set up within the defaults of your XCode application. I'll explain that later, though.

So let's set up you own custom listing for files...

First Quit XCode and the Property List Editor applications, without saving any changes.

Now go to the /Library/Application Support/Apple/Developer Tools/File Templates/ in the Finder and create a new sub folder named "- My Classes". Now go to the Cocoa folder and copy the 'Objective-C class.pbfiletemplate' to the "- My Classes" folder. Now open XCode and create a new Cocoa Application project. Now add a new file to the project. Notice you'll see a heading of "- My Classes" with an "Objective-C class" listing. Just cancel for now, as we'll go ahead an make some further changes.

Rename the 'Objective-C class.pbfiletemplate' to 'NSObject subclass.pbfiletemplate'. Then open the TemplateInfo.plist with that folder.

Edit the Description listing under the Root element to say 'An Objective-C class file which subclasses NSObject, with an optional header which includes the header.'

And finally, edit the .m file to use /* and */ to hold the comments rather than the normal // for each line.

Now close and save both the .h file and the TemplateInfo.plist file.

Now attempt to add a new file to your XCode project. You'll see the "- My Classes" heading with the "NSObject subclass" listing. Select the listing and you'll see the new description show up. Now click next, give the file and name and click finish. Notice the new commenting style on the .m file's comments.

The quick and easy way to take care of that "__MyCompanyName__" information is to use the Terminal.

Enter the following line in the Terminal, replacing the Company Name with whatever you prefer.
defaults write PBXCustomTemplateMacroDefinitions '{"ORGANIZATIONNAME" = "Company Name";}'

This page is powered by Blogger. Isn't yours?