Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
9,560
6,059
I want a subclass NSView that actually wraps around an NSSplitView containing a few other views. The problem is, the split view is ignoring any directions to set the position of its dividers (marked in red):

Code:
- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setup];
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self setup];
    }
    return self;
}

- (void) setup
{
    self.verticalSplitView = [NSSplitView new];
    self.verticalSplitView.dividerStyle = NSSplitViewDividerStyleThin;
    [self.verticalSplitView setVertical:YES];
    NSTextView *textView1 = [NSTextView new];
    NSTextView *textView2 = [NSTextView new];
    NSTextView *textView3 = [NSTextView new];
    textView1.backgroundColor = [NSColor redColor];
    textView2.backgroundColor = [NSColor blueColor];
    textView3.backgroundColor = [NSColor magentaColor];
    [self.verticalSplitView addSubview:textView1];
    [self.verticalSplitView addSubview:textView2];
    [self.verticalSplitView addSubview:textView3];
    [self addSubview:self.verticalSplitView];
    self.verticalSplitView.translatesAutoresizingMaskIntoConstraints = NO;
    [self addConstraints:[NSLayoutConstraint constraintsFillingChild:self.verticalSplitView]];
    [color=RED][self.verticalSplitView setPosition:40 ofDividerAtIndex:1];
    [self.verticalSplitView setPosition:20 ofDividerAtIndex:0];[/COLOR]
}

- (void)drawRect:(NSRect)dirtyRect
{
    [[NSColor redColor] setFill];
    NSRectFill(dirtyRect);
}

I also wrote this category method on NSLayoutConstraint:

Code:
+ (NSArray *)constraintsFillingChild:(id)view
{
    NSDictionary *views = NSDictionaryOfVariableBindings(view);
    NSArray *v = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|"
                                                         options:0
                                                         metrics:nil
                                                           views:views];
    NSArray *h = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|"
                                                         options:0
                                                         metrics:nil
                                                           views:views];
    NSMutableArray *constraints = [[NSMutableArray alloc] initWithCapacity:4];
    [constraints addObjectsFromArray:v];
    [constraints addObjectsFromArray:h];
    return constraints;
}

I tested this by dragging an NSView into a window in MainMenu.xib and assigning it to my subclass.

I've set a variety of values for the methods I highlighted in red and no matter what they seem to be ignored. I know they're being executed because everything else in that method works.
 

heyadrian

macrumors member
Aug 14, 2011
83
0
What you're looking for is:

setPositionOfDividerAtIndex:

i.e.

http://developer.apple.com/library/...stm/NSSplitView/setPosition:ofDividerAtIndex:

Another way of dealing with this is by having an NSSplitViewDelegate :)

If you do set the position and are using constants, I'd suggest using a delegate as you need to handle the screen resizing to resize the subviews accordingly so they don't break constraints.

Here's an example of one of my split view delegates which I use in one of my apps. It's on a 2 paned split view, but it still applies!

SplitDelegate.h file:


Code:
#import <Foundation/Foundation.h>

@interface SplitDelegate : NSObject <NSSplitViewDelegate>

@end

SplitDelegate.m file

Code:
#import "SplitDelegate.m"

@implementation SplitDelegate

-(CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex{
    return 200.0; [B]//controls the MAX position of a split (use an if else or switch block to deal with the dividerIndex)[/B]
}

-(CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex{
    return 200.0;[B]//controls the MIN position of a split (use an if else or switch block to deal with the dividerIndex)[/B]
    
}

-(CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex{
    return 200.0;  [B]//This deals with a FIXED splits (so for multiple use an if else or switch block to handle divider Index)[/B]
}


[B]//The following handles resizing of the window and how the subviews and splits and panes are affected. This is specifically for a 2 pane splitView[/B]
-(void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:
(NSSize)oldSize
{
    CGFloat dividerThickness = [sender dividerThickness];
    NSRect leftRect  = [[[sender subviews] objectAtIndex:0] frame];
    NSRect rightRect = [[[sender subviews] objectAtIndex:1] frame];
    NSRect newFrame  = [sender frame];
    
    leftRect.size.height = newFrame.size.height;
    leftRect.origin = NSMakePoint(0, 0);
    rightRect.size.width = newFrame.size.width - leftRect.size.width
    - dividerThickness;
    rightRect.size.height = newFrame.size.height;
    rightRect.origin.x = leftRect.size.width + dividerThickness;
    
    [[[sender subviews] objectAtIndex:0] setFrame:leftRect];
    [[[sender subviews] objectAtIndex:1] setFrame:rightRect];
}


[B]//The following deals with a custom divider and the effective Rect that the divider lives in i.e. where you click with the mouse to move it left or right[/B]
- (NSRect)splitView:(NSSplitView *)splitView effectiveRect:(NSRect)proposedEffectiveRect forDrawnRect:(NSRect)drawnRect ofDividerAtIndex:(NSInteger)dividerIndex{
    return NSMakeRect(drawnRect.origin.x, drawnRect.origin.y, 1, drawnRect.size.height);
}

@end

Hope this helps :)

Cheers,

A

----------

Just to add, is there any reason why you're just not making a nib with a delegate? That's a hell of a lot easier than messing about with constants :D Even if you have a custom nib, it can still be a subclass of NSSplitView :) The reason why I say go that method is because it sets up most of the constructors for you before hand rather than having manually hand-punch it all in.
 

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
9,560
6,059
The reason I'm doing it by hand is because I need to be able to generate several of these views, whereas generally I only use nibs for static parts of my interface, but now that you've suggested it, I guess I could use a nib... Maybe... I'll think about it.
 

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
9,560
6,059
Okay - I've solved the problem, and I believe I know what accounts for this behavior within Apple's own code.

When you call setPosition: ofDividerAtIndex:, it calls the delegate of the split view to check if the value you've passed in is valid. Specifically, it'll call splitView:constrainMinCoordinate: ofSubviewAt:.

I believe their exact implementation resembles something like:
Code:
CGFloat minimum = [delegate splitView:self constrainMinCoordinate:(...) ofSubviewAt:(...)];

But if delegate isn't set, this returns 0. Similarly, constrainMaxCoordinate: returns 0. Thus if there's no delegate for a split view, it will never accept widths other than 0.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.