CocoaDev

Edit AllPages

Here’s some simple code to save and restore the position of an NSSplitView (which unfortunately don’t save their position). You can call these from your window controller’s awakeFromNib/windowDidLoad and termination code.

@interface NSSplitView (Defaults)

- (void) saveLayoutWithName: (NSString *) defaultName;
- (void) loadLayoutWithName: (NSString *) defaultName;

@end

@implementation NSSplitView (Defaults)

- (void) saveLayoutWithName: (NSString *) defaultName
{
    NSMutableArray *rectstrs = [NSMutableArray array];
    for (NSView *view in self.subviews)
        [rectstrs addObject: NSStringFromRect(view.frame)];
    [[NSUserDefaults standardUserDefaults] setObject: rectstrs forKey: defaultName];
}

- (void) loadLayoutWithName: (NSString *) defaultName
{
    NSMutableArray *rectstrs = [[NSUserDefaults standardUserDefaults] objectForKey: defaultName];
    if (rectstrs == nil) return;

    for (NSView *view in self.subviews) {
        view.frame = NSRectFromString([rectstrs objectAtIndex:0]);
        [rectstrs removeObjectAtIndex:0];
    }
    
    [self adjustSubviews];
}

@end

Too much work to change, easier to rewrite. This also handles collapsed views:

This only sort of handles collapsed views. “Collapsed” state is different from just zero height (or width). In a freshly loaded layout with this code, no subview will report YES for -[splitView isSubviewCollapsed:subview].

@interface NSSplitView (CCDLayoutAdditions)

- (void)storeLayoutWithName: (NSString*)name;
- (void)loadLayoutWithName: (NSString*)name;

@end    

@implementation NSSplitView (CCDLayoutAdditions)

- (NSString*)ccd__keyForLayoutName: (NSString*)name
{
	return [NSString stringWithFormat: @"CCDNSSplitView Layout %@", name];
}

- (void)storeLayoutWithName: (NSString*)name
{
	NSString*		key = [self ccd__keyForLayoutName: name];
	NSMutableArray*	viewRects = [NSMutableArray array];
	NSEnumerator*	viewEnum = self subviews] objectEnumerator];
	[[NSView*			view;
	NSRect			frame;
	
	while( (view = [viewEnum nextObject]) != nil )
	{
		if( [self isSubviewCollapsed: view] )
			frame = NSZeroRect;
		else
			frame = [view frame];
			
		[viewRects addObject: NSStringFromRect( frame )];
	}
	
	[[NSUserDefaults standardUserDefaults] setObject: viewRects forKey: key];
}

- (void)loadLayoutWithName: (NSString*)name
{
	NSString*		key = [self ccd__keyForLayoutName: name];
	NSMutableArray*	viewRects = [[NSUserDefaults standardUserDefaults] objectForKey: key];
	NSArray*		views = [self subviews];
	int				i, count;
	NSRect			frame;
		
	count = MIN( [viewRects count], [views count] );
	
	for( i = 0; i < count; i++ )
	{
		frame = NSRectFromString( [viewRects objectAtIndex: i] );
		if( NSIsEmptyRect( frame ) )
		{
			frame = views objectAtIndex: i] frame];
			if( [self isVertical] )
				frame.size.width = 0;
			else
				frame.size.height = 0;
		}
			
		[[views objectAtIndex: i] setFrame: frame];
	}
}

@end

I’ve got code similar to ones above that set the position of a split view. Now this works fine for a normal split view, but for a split view inside another split view, this doesn’t work. Can anyone see why it might not work? I’ve tried using setNeedsDisplay: and adjustSubviews but neither worked. Thanks.

- (void)setPositionUsingAutosaveName:([[NSString *)name
{
	NSArray *s = [[NSUserDefaults standardUserDefaults] objectForKey:name];;
	if (s == nil || [s isKindOfClass:[NSArray class]] == NO)
		return;
	
	NSArray *subviews = [self subviews];
	int i;
	for (i=0; i<[subviews count] && i<[s count]; i++)
	{
		NSRect newRect = NSRectFromString([s objectAtIndex:i]);
		[[subviews objectAtIndex:i] setFrame:newRect];
	}
}