On Mar 26, 2008, at 11:52 AM, Matthew Miller wrote:
Could some please explain in simple terms how to create a NSMatrix which distributes cells in rows based on the input of a table and takes away the cells when it is not needed. Thank you very much!

P.S. I am new to cocoa so please don't use really big and complex cocoa vocab, but then again, I would appreciate anything! :)

The problem is that NSMatrix is not a beginner class. If you don't have a solid understanding of other Cocoa frameworks like NSControl, NSView, NSCell (and its variants- NSActionCell, NSImageCell), and if you aren't comfortable subclassing yet, you're going to have a lot of trouble with NSMatrix.

But, here's what you need to do.

First, you need to do some design.

1. Create a subclass for the data that goes in the cells. While not strictly necessary (e.g. you could create a matrix of strings using just NSString as the object class), you almost always will want to do this. Typically you'll have information that won't be displayed, such as a path to an image, and you'll want to keep that connected to the data you are displaying. So, we'll create an NSObject subclass to hold our data and call it ThumbnailObject.

2. Decide where you're going to store that data. NSArrayController is a great way to manage contents of an NSMatrix, but if you're not familiar with that, I'd go with an NSMutableArray for now. Your ThumbnailObjects will be stored in the array as they are created, and removed from it when they're tossed aside. The Matrix will reflect the objects that are stored in this array.

3. Decide what your cell class will be for the matrix. For example, if you were displaying a bunch of images, you might use NSImageCell. If you were displaying text, you might use NSTextFieldCell. If you're displaying checkboxes, you might use NSButtonCell. However, unless your matrix needs are very basic, you'll probably want to subclass this base cell class. Do you absolutely need to? Maybe not, but it will probably make your code easier to follow and maintain. So, let's say you're going to display images. You'd probably want to subclass NSImageCell or NSActionCell for that. Which you choose is an implementation detail, and determines the methods you need to override or provide in order to have the cell behave the way you want. For this discussion, we'll create a subclass of NSImageCell called ThumbnailCell.

4. Create a subclass of NSMatrix. You'll almost certainly need this, since NSMatrix is a rather peculiar class that almost feels "unfinished" when you get working with it. We'll call our subclass of NSMatrix ThumbnailMatrix.

So, in this design, you have a ThumbnailMatrix which contains ThumbnailCells that display data from ThumbnailObjects. You've created subclasses of NSMatrix, NSImageCell and NSObject to do this.

You've got to get the design above nailed down before going any further. If any of that is confusing or doesn't make sense to you, read up on the classes mentioned above before proceeding.

Now you code.

5. Code the subclasses.

The ThumbnailMatrix class is going to have the following methods:

initWithFrame - Create an instance of ThumbnailCell and pass it to the superclass's initWithFrame method as the prototype cell for your matrix.

awakeFromNib - Here, I'd set up things like interCellSpacing, cellSize, autoScroll, etc.

renewRowsIfNeeded - This is a method I create that checks to see if the number of cells that can fit on the rows has changed or if the width between them has changed. (I usually spread the extra space at the end of the row between the cells to make them appear evenly distributed.) Get the cell size and the super view's bounds, then determine how many rows and columns fit. Get the "left over" space from a full row and use that to determine what the interCell spacing should be, then set the interCell spacing if it's different. Compare the number of rows and columns to what you already have. If they're different you need to call renewRows. Return true if the intercell spacing changed or renewRows was called.

setFrameSize - Override this if you want to have the matrix do live rebuilding as you grow or shrink the window. If you don't do this, the matrix will stay it's old size until the mouse button is released, at which point it will change its number of rows and columns to fit (because of viewDidEndLiveResize below). It looks nice to have the matrix change to reflect the final state while you're dragging, so I'd implement this. It should call it's super then, if renewRowsIfNeeded returns true, call sizeToCells. Then call setNeedsDisplay (or setNeedsDisplayInRect if you're optimizing for live resizing.) Do setNeedsDisplay for now, then look into live resizing optimization.

viewDidEndLiveResize - When the mouse is released after a resize, this method is called. If renewRowsIfNeeded returns true, then call sizeToCells, setNeedsDisplay.

rebuildThumbnailMatrix - This is a method I'd implement to get your data into the matrix. It basically needs to go through the array mentioned in step 2, create enough cells if there aren't enough already, then store a ThumbnailObject in each cell. At the end, you need to do the renewRowsIfNeeded, sizeToCells, setNeedsDisplay mantra.

There are several other things you'll probably end up wanting to do, for example to support drag and drop. But, get this stuff working first and then search the lists about that.

The ThumbnailCell class is going to have these methods:

dealloc - releases stored thumbnailObject, calls super
copyWithZone - returns a new cell from super copyWithZone, retaining the ThumbnailObject and storing that in the new cell
thumbnailObject - returns the stored thumbnailObject
setThumbnailObject - stores the cell's thumbnailObject
image - returns the stored thumbnailObject's image
setImage - sets the stored thumbnailObject's image and sends it to super setImage drawInteriorWithFrame - if no thumbnailObject is set, drops out. (A matrix can have unused cells at the end of the last row) Otherwise, checks to see if [super image] == [self image]. If not, call [super setImage: [self image]] to store the thumbnailObject's image in the super. Either way, call super drawInteriorWithFrame if the thumbnail object for the cell is not nil.

You'll probably want to override more methods to control drawing.

The ThumbnailObject class is going to simply have storage and accessors for your thumbnail data. In this case you might have
init - create an NSMutableDictionary to hold the object's data
dealloc - clean up
image - return the dictionary's entry for the object's image
setImage - store the passed image in the dictionary
pathToImage - return the dictionary's entry for the image's path.
setPathToImage - store the passed image path in the dictionary

This is by no means an optimal way to do things since eventually you'd have all of your images in memory. However, you could implement some "smarts" to unload images that are well out of view range and reload them when they're asked for, etc. Get it working, then optimize it.

So, at this point you have all of your classes created. In your app controller, you'll need an outlet to your matrix. At some point you'll call rebuildThumbnailMatrix (above) with the data you want to put in the matrix. If you delete objects or add more later, you'll want to call rebuildThumbnailMatrix again.

Now you create a matrix in InterfaceBuilder.

6. Go to Interface Builder and create a matrix in a window. In IB3 you'll create an instance of an NSImageView and then embed it in a matrix and then embed that in a scroll view. Change the matrix's type to ThumbnailMatrix. Connect your application controller's outlet to the matrix.

7. Build and debug and debug and debug.
8. Debug more, you missed something.
9. It works, optimize, you're done.

Of course there are other ways to do some of this, but that's the basic approach you'll need to follow.

I think this is probably way beyond where you're at right now with Cocoa, but use it as a guide for how to approach learning the material. It probably looks overwhelming, but once you're comfortable with the underlying Cocoa frameworks it will be manageable.

- d

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to [EMAIL PROTECTED]

Reply via email to