The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








I heart FeedBurner

Latest Diggs - Programming

Computers Blogs - Blog Top Sites

Site Hits

Total: 3,822,723
since: 19 Jan 2005

Leopard Sample: A Bound NSCollectionView

posted Sun 28 Oct 07

Leopard introduces a bunch of amazingly powerful new controls, but one of my favorite new controls is the NSCollectionView. This control works a lot like the FlowLayoutPanel if you're familiar with Windows Presentation Foundation (WPF). It essentially is a layout container responsible for laying out a collection of subviews. You can either manually create the subview collection, or you can set the content array of the NSCollectionView. This is a really powerful option because if you can set the content array, you can also bind it. For this demo, I've bound the content array of the NSCollectionView to an array controller. If you follow along (or if you cheat and just download the code), you'll notice that the NSCollectionView subviews automatically request Core Animation layers. This means that, by default, new items fade in as they are added, but you can change that transition using the animations tab of the inspector.

To get started, just create a new Cocoa application. The first thing you do after creating the new Cocoa application is edit the project settings and set Garbage Collection to Required. Why you ask? Well, as a .NET developer, I have taken a blood oath to never write unmanaged code. If I violate this blood oath, I'm fairly certain that it will rip a hole in the fabric of space-time from which none of us will escape. So yeah, make sure GC is Required. The Cocoa application we're building is called MonsterCompendium. Rather than doing some contrived Hello World app, this app forms the basis of an app that a pen-and-paper DM might use to keep track of monsters. Obviously it's ridiculously oversimplified, but you get the idea.

I'm going to assume that you've done some Cocoa programming before (mostly because I don't have enough space to write the tutorial in a single blog post... maybe I should do a screencast?). Create yourself a new class called AppController and give it a method called newMonster and then make sure that you've got a "plus" button on your main window that invokes newMonster. Create a new class called KHMonster (feel free to change the initials so they match your name... or keep my initials if you really like them...). The source code to KHMonster.h and KHMonster.m is listed below. Take special note of the new property syntax and the fact that I've not written any retain/release code:

 #import <Cocoa/Cocoa.h>

 

 

@interface KHMonster : NSObject 

{

    NSString *trueName;

int armorClass;

int attackRating;

}

 

@property(copy, readwrite) NSString *trueName;

@property int armorClass;

@property int attackRating;

 

@end

And here's the implementation (.m) code:

 #import "KHMonster.h"

@implementation KHMonster

 

@synthesize trueName;

@synthesize armorClass;

@synthesize attackRating;

@end

With that out of the way, you can drag a new NSCollectionView onto your main window. Once you do this, you should notice that you get some extra goodies in your MainMenu.nib tray: an instance called Collection View Item and an NSView that represents the template for a single item within the collection. When you double click the NSView, define an interface that will be bound to a single instance of KHMonster. To do that, you need to set the value of your text fields to be bound to the Collection View Item (this is a special instance that acts like a proxy) with the model root path of : representedObject.trueName. representedObject is a property that points to the object being represented by that particular instance of the collection view item and trueName is a property on the KHMonster class.

In case you're curious, here's the code to the newMonster: method on the AppController class:

#import "AppController.h"

#import "KHMonster.h"

 

@implementation AppController

 

- (IBAction)newMonster:(id)sender

{

KHMonster *newMonster = [[KHMonster alloc] init];

newMonster.trueName = [NSString stringWithString:@"New Monster"];

newMonster.attackRating = 12;

newMonster.armorClass = 30;

[monsterArray addObject:newMonster];

}

@end 

Note that I've got an array controller that is serving up instances of the KHMonster class. My AppController instance has an IBOutlet called monsterArray that I've rigged up through Interface Builder to allow the app controller to refer to the array controller as in the preceding code.

Finally, after all the code is together and all the bindings are rigged up, I can run the application. In the screenshot below, you can see me editing a borderless bold text field that contains the monster's name (bound to representedObject.trueName) and a couple labels and some text fields with number formatters in them to allow them to be bound to integers properly. 

 Monster Compendium Demo - Modifying newly added monster rows

And in the next screenshot you see the collection view after editing a couple more items. The reason this is important is to show you that I'm not editing the same thing, but that I am actually working with individual elements within an array. 

 Monster Compendium Demo - Modified Rows

If you're familiar with Cocoa, then you know that this sample is only a few minutes worth of work away from being able to run against a local Core Data data source instead of the manual array controller of KHMonster objects.

I highly recommend you download the full sample and then go through the blog post again to see how the sample was created. Once you get the hang of using the NSCollectionView, you'll start to wonder how you ever lived without it. The keys to remember are:

 

  • If you are going to turn on Garbage Collection, you have to do it for every build config such as Debug and Release. The change will not automatically propagate from your debug config to your release config.
  • Remember representedObject. It is your friend. It's better than your friend, except it doesn't buy you beer. Since your friend probably doesn't buy you beer anyway, representedObject is definitely better than your friend.
Here's a link to the code download:
monstercompendium2.zip

tags:              

links: digg this    del.icio.us    technorati    reddit

AddThis Social Bookmark Button




1. cal left...
Sun 28 Oct 07 9:57 pm

I've been trying something similiar to your example - using core data entities instead of creating my own item classes. After setting up the NSCollectionView, and utilising representedObject on the subview, I can display views with data bound to attributes of the objects in my nsarraycontroller (looking similiar to your example). Sweet. Now ... what I'd really like, is to be able to use this collectionview as a kind of selectable list ... so, upon selecting a listitem view in the collectionview, the selection of the bound arraycontroller would change, and my interface could respond appropriately.

I can't see to get this working ... any ideas?


2. Ahruman left...
Mon 29 Oct 07 3:54 am :: http://jens.ayton.se/blag/

Some other nice new features in Leopard: template images, and the fact that there are some default ones. For instance, instead of naming your buttons “+” and “-”, you could set their image to NSAddTemplate and NSRemoveTemplate, respectively. Just for kicks, try setting the button behaviour to Toggle (but don’t leave it that way for +/- buttons).


3. Kevin Hoffman left...
Mon 29 Oct 07 5:28 am

Sweet. I had completely forgotten about the template images. That's one thing that I thought was missing when using Tiger was the ability to use some stock images/looks without jumping through flaming hoops.

I just tried it out on my NSCollectionView sample. Very nice!


4. rd left...
Mon 29 Oct 07 10:08 pm

newMonster.trueName = ; or

newMonster.trueName = @"New Monster";


5. Robert Stainsby left...
Tue 30 Oct 07 5:53 am :: http://web.mac.com/rjsdev

Thanks - my first Obj-C 2.0 project!

Am I right in thinking that we're supposed to be using NSInteger instead of int now (for 32/64 bit independence)? :-)

I really enjoy your blog. It's great to get a cross-platform perspective.


6. Kevin Hoffman left...
Tue 30 Oct 07 7:56 am

Yes, you should probably be using NSInteger if you plan on doing cross-architecture coding.

As for the @"New Monster" thing, I tried that during previous builds and Xcode used to complain violently that I could not initialize a string that way... oh well, thanks for the heads-up.


7. Martin Pilkington left...
Thu 01 Nov 07 5:34 pm :: http://www.mcubedsw.com

Even though showing several Obj-C 2.0 concepts is probably better for a post like this, it's worth mentioning that the same project could be made using Core Data with zero code and with the ability to save/load and undo changes.


8. Kevin Hoffman left...
Thu 01 Nov 07 7:22 pm

Yes, you'll note that I said it could all be done using Core Data and not writing any code. The point, for me anyway, was to do it the "hard" way first, then go to Core Data afterward. I've found that going straight to Core Data as a newbie makes me skip some of the important details of MVC and how bindings work. So yes, it would be easier to do it no-code with Core Data, but far less illustrative of some key concepts.


9. RONZILLA left...
Fri 02 Nov 07 1:01 pm

I haven't opened the project, but I presume it requires binding the content of the collection view to the array controller's arrangedObjects. To keep the view selection synched with the array controller's selection just bind the collection view's selectionIndexes binding to the arraycontroller's selectionIndexes property.

You can then also modify the collection view item prototype view to include some visual element indicating "selection" (like a box) and bind its transparent binding to collectionview item's representedObject.selected. (don't forget to use the nsnegateboolean value transformer on the transparent binding)


10. Jon left...
Sun 04 Nov 07 2:55 pm

Why copy the trueName property? Since you're creating a new KHMonster for each monster, I wouldn't think having a separate copy of the ivar would matter. Or is there some other reason I'm missing here?


11. JK left...
Mon 12 Nov 07 3:08 pm

What is wrong with NSCollectionView and tables?

Set up a NSTableView and bind the columns to the Monster Controller keys. Put it in the window next to the +/- buttons. Every time you push the + -button, new row appears with the new monster. Just the way it should.

Now set up a similar table in the Monster View. Every time you add a monster, new table appears in the collection view. A new row appears in all the tables, but in Monster Views the rows are empty. The same row is selected in every table, but no data is drawn in tables inside the collection view.

Bug?


12. Michael Martz left...
Tue 13 Nov 07 8:02 pm

In response to Jon... Kevin used copy for trueName to ensure that the value would be protected from accidentally being changed outside the KHMonster class. The actual setter would use something like "trueName = ;" while the getter would use something like "return ;".


13. JRoberts left...
Mon 17 Nov 08 1:41 pm :: http://www.cypressworld.com/

Thanks so much for this example. I am an ancient professional programmer carbon-dated to the period before MS-DOS or even PC's. What I am not is a professional Cocoa researcher but it seems I've spent 75% of the last 3 years doing just that, searching blogs and user websites for hints on how to use Apple's wonderfully powerful but insanely complicated and miserably documented development system.

Most of my apps are designed to control the line of data-gathering devices my company designs and manufactures. All of our hardware architectures support 32 devices per network so you can imagine how tedious it would be managing a view for each device that includes subviews like an NSLevelIndicator, a status icon and text, a name field, etc. Learning how to use NSCollectionView in just one day from your example has caused much happiness in the mouth!


14. N45800 left...
Tue 18 Nov 08 8:55 pm

Just a quick note, delete the build folder before posting source code. It is automatically re-created the next time you build the app, and drops the size of the .zip down to 72KB from 2.2MB. Or, set the "Place Intermediate Build Files" setting in Xcode to some temporary directory that is regularly cleaned. It may make programs take a bit longer to compile (whenever the intermediate / temporary files are deleted), but you save a ton of space (relatively).


Tag Related Posts

Upgrading your Leopard install to Java SE 6 64-Bit

Mon 12 Jan 09 1:38 P GMT-05
tags:            

Apple drops the iPhone NDA for Released Software

Wed 01 Oct 08 3:54 P GMT-05
tags:          

Cappuccino, Objective-J, and You

Wed 10 Sep 08 6:14 P GMT-05

So I'm in the LA Times ;)

Wed 27 Aug 08 2:51 P GMT-05
tags:                  

MobileMe vs. Live Mesh Throwdown - Round 1

Wed 16 Jul 08 10:33 A GMT-05

Building Model Classes in C# and Cocoa

Sun 15 Jun 08 3:13 P GMT-05
tags:            

MobileMe vs. Live Mesh - Round 1

Wed 11 Jun 08 12:20 A GMT-05

My Macbook Air is masculine, dammit!

Mon 17 Mar 08 6:59 P GMT-05
tags:          

iPhone Underrated as a Gaming Device?

Fri 14 Mar 08 1:50 P GMT-05
tags:        

My take on the iPhone SDK

Sat 08 Mar 08 1:39 P GMT-05

Jobs says "not likely" to Flash on the iPhone

Thu 06 Mar 08 1:39 A GMT-05
tags:          

My Macbook Air Review

Sun 02 Mar 08 4:20 P GMT-05

iPhone Roadmap March 6th

Fri 29 Feb 08 10:41 P GMT-05
tags:        

Video of the Macbook Air in Action

Wed 20 Feb 08 3:04 P GMT-05

Macbook Airはきれいですよ!

Sun 17 Feb 08 2:38 A GMT-05

Why is O'Reilly Condoning iPhone Hacking?

Mon 11 Feb 08 3:55 P GMT-05

Evaluating my next laptop purchase

Wed 06 Feb 08 8:40 P GMT-05

The iPhone SDK key has been leaked! Oh Noez!!!1

Tue 29 Jan 08 11:36 A GMT-05
tags:        

Why Geeks just don't "get" the Macbook Air

Thu 17 Jan 08 2:30 P GMT-05

Popcorn + TiVo + Macbook Pro + iPhone == Hell Yeah!

Tue 15 Jan 08 3:11 P GMT-05
tags:          

How my ADC membership changed my life

Mon 31 Dec 07 3:57 P GMT-05
tags:      

Leopard Code Sample : Sprinkling in some Bonjour

Tue 27 Nov 07 2:32 P GMT-05
tags:        

Leopard Sample: A Bound NSCollectionView

Mon 29 Oct 07 1:41 A GMT-05

My Leopard Installation Experience

Sun 28 Oct 07 12:57 P GMT-05
tags:    

Leopard is out - let the code samples begin!

Fri 26 Oct 07 10:09 A GMT-05
tags:          

My life is complete : iPhone SDK is CONFIRMED.

Wed 17 Oct 07 6:38 P GMT-05
tags:          

Leopard Shipping October 26th!!

Tue 16 Oct 07 4:59 P GMT-05
tags:        

My iPhone Review

Mon 23 Jul 07 1:09 P GMT-05
tags:        

Microsoft Codename Acropolis - Unwrapped

Wed 20 Jun 07 3:22 P GMT-05
tags:              

The dreaded language bleed-over has begun

Tue 19 Jun 07 6:23 P GMT-05
tags: