10

I have the default NSWindow created in a new application which has a single NSView. I then create a new NSViewController which has it's own XIB and a view. In the app delegate I do the obvious

self.mainViewController = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];
[self.window.contentView addSubview:self.mainViewController.view];
self.mainViewController.view.frame = ((NSView*)self.window.contentView).bounds;

OK, how do I set a constraint in the new way to have my subview keep its size identical to the Window, i.e. it's superview. It doesn't seem to work automatically. Autoresizessubviews is ON for both views.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
ahwulf
  • 2,584
  • 15
  • 29
  • Are you intending to do this in IB, or in code? – Peter Hosey Nov 14 '12 at 09:35
  • I think I will avoid this issue entirely by keeping the UIWindow and view in the same xib file. Plus I need to use a NSWindowController instead of a NSViewController (too much iOS on the brain) – ahwulf Nov 16 '12 at 14:20

6 Answers6

8

Basically, you need to constrain four things:

  1. The leading space of your subview to its superview to be zero
  2. The top space of your subview to its superview to be zero
  3. The width of your subview to be equal to its superview's width
  4. The height of your subview to be equal to its superview's width

If the visual constraint isn't working out for you, you can build these four constraints individually in code. Use the method +constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier: constant: to specify exact relationships between different views' attributes. For example, constraint #1 above might be expressed by:

[NSLayoutConstraint constraintWithItem:mySubview
                             attribute:NSLayoutAttributeLeading
                             relatedBy:NSLayoutRelationEqual
                                toItem:mySuperview
                             attribute:NSLayoutAttributeLeading
                            multiplier:1.0f
                              constant:0.0f]

and #3 might be:

[NSLayoutConstraint constraintWithItem:mySubview
                             attribute:NSLayoutAttributeWidth
                             relatedBy:NSLayoutRelationEqual
                                toItem:mySuperview
                             attribute:NSLayoutAttributeWidth
                            multiplier:1.0f
                              constant:0.0f]

Once you've built up those four constraints, you can add them to your superview as needed.

Note that there are multiple ways to achieve the same effect as above:

  • You might constrain the trailing space and bottom space instead of the width and height
  • You might constrain the center X and center Y instead of the leading and top spaces

You can also probably come up with the same constraints in a visual representation, as in Peter Hosey's answer. For example, an equal-width constraint might look like @"[mySubview(==mySuperview)]" with the appropriate views dictionary.

Keep in mind that the Auto Layout Guide is a wealth of information about constraints, including how to debug them when things go wrong.

Tim
  • 59,527
  • 19
  • 156
  • 165
  • I already solved the issue by keeping everything in a single xib file but I like this answer. I can't mark it until I find time to try this myself. – ahwulf Dec 04 '12 at 14:16
4

In the nib editor, drag the subview's size until it is the same size as its superview. Xcode will create an appropriate width constraint automatically.

In code, I would try |-0-[mySubview]-0-| (adapted from the example in the constraint syntax documentation).

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • I know I can do this, however the subivew is located in a different XIB. I tried the visual constraint but all it does is constrain the parent to be no smaller than the child. Thinking it must be the wrong constraint syntax (that stuff is quite confusing so far) – ahwulf Nov 14 '12 at 20:48
  • @ahwulf: A constraint has to be able to refer to both objects, so you can't create a constraint in a nib if the objects are in different nibs—because which nib would you create it in? – Peter Hosey Nov 14 '12 at 23:06
  • Yes, it needs to be in code, but so far it's not obvious what it should look like. – ahwulf Nov 15 '12 at 15:21
2

Just like Peter wrote, you should use the visual format language.

When doing this, however, order is important: when you create a constraint, all views it references have to be part of the same view hierarchy.

So given your example, the code would have to become:

self.mainViewController = [[MainViewController alloc] initWithNibName:@"MainViewContoller" bundle:nil];
NSView *containerView = self.window.contentView;
NSView *contentView = self.mainViewController.view;
[contentView setTranslatesAutoresizingMaskIntoConstraints: NO];
[containerView addSubview:contentView];

NSDictionary *viewBindings = NSDictionaryOfVariableBindings(contentView);
[containerView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:nil views:viewBindings]];
[containerView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:nil views:viewBindings]];
Luke
  • 4,908
  • 1
  • 37
  • 59
danyowdee
  • 4,658
  • 2
  • 20
  • 35
0

You can override setContentView:, contentView:, and contentRectForFrameRect: so they will deal with window.frame - sized view.

Nickolay Olshevsky
  • 13,706
  • 1
  • 34
  • 48
  • How does overriding methods of NSWindow help with constraining a view to always be the same size as its superview? – Peter Hosey Dec 11 '12 at 10:57
0

If you're ok with using a 3rd party library, you can accomplish this with ReactiveCocoaLayout in one simple line:

RAC(view,rcl_frame) = parentView.rcl_frameSignal;
zakdances
  • 22,285
  • 32
  • 102
  • 173
-1

I had the same problem and I ended up with this solution which is working with SDK 10.10. Just set the autoresizingMask of the new view to be the same as the parent window. Only one row of code and it works like a charm...

self.masterViewController = [[MasterViewController alloc] initWithNibName:@"MasterViewController" bundle:nil];

[self.window.contentView addSubview:self.masterViewController.view];
self.masterViewController.view.frame = ((NSView *)self.window.contentView).bounds;
self.masterViewController.view.autoresizingMask = ((NSView *)self.window.contentView).autoresizingMask;
Saeverix
  • 397
  • 5
  • 18