Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto Layout of a NSView in a NSScrollView embedded in a NSSplitView

I can't seem to figure out how to get the content inside a NSScrollView to scale properly. I have come across a few references which describe general approaches, but nothing concrete.

Ambiguous Layout

This seems to indicate that NSScrollView and Auto Layout don't play nicely together. It is somewhat telling that both the iOS & OSX Auto Layout guide examples are UIScrollView instead of NSScrollView:

Auto Layout Examples

I threw together an example. The main window contains a split view with some fixed content on the right and a view that will be filled in programmatically on the left. (I would post a picture, but I don't have the required reputation yet). Running the code below, any attempt to move the splitter will result in it snapping back to the original position.

AppDelegate.h

#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (weak) IBOutlet NSScrollView *scrollView;
@property (strong, nonatomic) NSView *containerView;
@property (strong, nonatomic) NSMutableDictionary *views;
@property (assign) IBOutlet NSWindow *window;

@end

AppDelegate.m

#import "AppDelegate.h"

@implementation AppDelegate

@synthesize scrollView;
@synthesize containerView;
@synthesize views;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
}

-(void)awakeFromNib {

// Create a container to hold all the subviews
containerView = [[NSView alloc] initWithFrame:NSZeroRect];
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
[scrollView setDocumentView:containerView];

views = [[NSMutableDictionary alloc] init];

for (int i=0; i<8; i++) {

    NSBox *box = [[NSBox alloc] init];
    NSString *title = [NSString stringWithFormat:@"Box%@", [[NSNumber numberWithInt:i] stringValue]];
    box.title = title;
    [views setObject:box forKey:title];
    [box setTranslatesAutoresizingMaskIntoConstraints:NO];
    [containerView addSubview:box];
}

long height = 160;

NSArray *sortedKeys = [[views allKeys] sortedArrayUsingSelector: @selector(compare:)];
for(NSString *viewName in sortedKeys) {

    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[[NSString alloc] initWithFormat:@"|-20-[%@]-20-|", viewName] options:0 metrics:nil views:views]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[[NSString alloc] initWithFormat:@"V:[%@(==%ld)]",viewName, height] options:0 metrics:nil views:views]];
}

// Build the subview-to-subview constraint string
NSString *constraintString = @"V:|";
for(NSString *viewName in sortedKeys) {
    constraintString = [constraintString stringByAppendingString:[NSString stringWithFormat:@"-20-[%@]",viewName]];
}

// Subview-to-subview constraints
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraintString options:0 metrics:nil views:views]];

// Container view constraints
NSDictionary *topLevelDictionary = NSDictionaryOfVariableBindings(containerView);
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-0-[containerView]-0-|" options:0 metrics:nil views:topLevelDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[containerView]" options:0 metrics:nil views:topLevelDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[[NSString alloc] initWithFormat:@"V:|[containerView(==%ld)]", (long)(20 + height) * sortedKeys.count + 20] options:0 metrics:nil views:topLevelDictionary]];
}

@end

Any direction would be greatly appreciated.

like image 393
Kurly Avatar asked Dec 25 '22 15:12

Kurly


1 Answers

I had this same issue. Autolayout and NSSplitView play very nicely together, you just need to sacrifice the right kind of chicken.

Try setting holding priority slightly higher on the relevant split portion. The image shows that my rightmost split will take priority.

enter image description here

Also another consideration. Is the content of the relevant split width constrained? You may need to set a greater-than-or-equal constraint on its width e.g |-0-[mysplitcontent(>=minwidth)]-0-|

like image 199
Warren Burton Avatar answered Dec 28 '22 04:12

Warren Burton