CocoaDev

Edit AllPages

I had need recently to lay out a ( varying ) number of procedurally-generated same-size elements in a window such that the elements would layout out in a grid with an optimal number of rows and columns for window dimensions. These had to be NSView subclasses since some of them had to be able to hold arbitrary controls and some were just text views.

Having written layout managers before for BeOS and for Qt & for Java I decided why not attempt to make a general purpose “flow layout” for Cocoa…

Here’s the header ( ZFlowLayout.h )

#import <Cocoa/Cocoa.h>

/* No spring will let elements stretch to fit width

Left spring will push elements to right side, 
with no stretching

Right spring will push elements to left side,
with no stretching.

Spring on left & right will squish layout into center. */ typedef enum _ZFlowLayoutSpring {
ZNoSpring = 0,
ZSpringLeft = 1,
ZSpringRight = 2,
ZSpringLeftRight = 3, } ZFlowLayoutSpring;

typedef struct _ZFlowLayoutSizing { NSSize minSize; int padding; int spring; bool oneColumn; } ZFlowLayoutSizing;

static inline ZFlowLayoutSizing ZMakeFlowLayoutSizing( NSSize minSize, int padding, int spring, BOOL oneColumn ) { ZFlowLayoutSizing sizing; sizing.minSize = minSize; sizing.padding = padding; sizing.spring = spring; sizing.oneColumn = oneColumn; return sizing; }

/************************** ZFlowLayout **************************/

@interface ZFlowLayout : NSView { ZFlowLayoutSizing _sizing; NSSize _lastSize; int _numElements; unsigned int _gridMask; BOOL _ignoreThisLayoutPass, _alternatingRowColors; NSColor *_backgroundColor, *_gridColor; }

/* Draw a solid background color */

/* Draw background using system alternating row colors */

@end

And here’s the implementation ( ZFlowLayout.m ):

#import “ZFlowLayout.h”

/************************** ZFlowLayout

ZFlowLayoutSizing _sizing;
NSSize _lastSize;
int _numElements;
unsigned int _gridMask;
BOOL _ignoreThisLayoutPass, _alternatingRowColors;
NSColor *_backgroundColor, *_gridColor;

**************************/

@interface ZFlowLayout (Internal)

@end

@implementation ZFlowLayout

/////////////////////////////////////////////////////////////////////// // Internal

/////////////////////////////////////////////////////////////////////// // Override NSView

@end

Usage is straightforward enough, instead of adding elements via IB, you just create your object and add it via [layout addSubview: someView];

–ShamylZakariya


I fixed a potential linker error situation with the ZMakeFlowLayoutSizing function. Strangely, it was linking from Objective-C++ just fine, but not pure Objective-C. –SZ


This is an awesome piece of code. Best example I’ve seen of a custom NSView. Thanks!

One nit - I think [self forceLayout]; is what we want inside frameSizeChanged. This makes it work nicely when resizing the window.


How can we add the ability to drag-reorder objects in a ‘pretty’ way? I mean, having everything slide out of the way smoothly like an NSToolbar, leaving room for the dropped element? 11/14/2005


Instead of using notifications to get size change uh…notifications it works better to override

(void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize;