Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

My ViewController creates his view by overwriting the loadView method:

- (void)loadView {
    UIView *view = [[UIView alloc] init];
    view.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
    self.view = view;
}

Now I'd like to switch to AutoLayout and therefore add an

view.translatesAutoresizingMaskIntoConstraints = NO;

to the loadView method. Now I have to specify the same constraints which were autogenerated before. My approach was to overwrite updateViewConstraints with

- (void)updateViewConstraints {
    if (0 == [[self.view constraints] count]) {
        NSDictionary* views = @{@"view" : self.view};

        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:0 views:views]];
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:0 views:views]];
     }

    [super updateViewConstraints];
}

But I get an exception because I think this kind of constraints should go with the super view:

*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view.  Does the constraint reference something from outside the subtree of the view?  That's illegal.

So, how do the correct Contraints have to look like?

share|improve this question
add comment

4 Answers

up vote 3 down vote accepted
+50

You need to set the constraints on the superview. The exception is caused by referencing the superview by passing "|" in the visual format. If you update your code like the following it will work:

- (void)updateViewConstraints {
    if (self.view.superview != nil && [[self.view.superview constraints] count] == 0) {
        NSDictionary* views = @{@"view" : self.view};

        [self.view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:0 views:views]];
        [self.view.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:0 views:views]];
    }
   [super updateViewConstraints];
}

In practice you'll probably want to check for something other than 0 constraints on the superview but this should help.

share|improve this answer
add comment

You don't have to set the constraints on your root view as Matt Neuburg explains the Chapter 19 of his Programming iOS 6 book, in section Manual Layout:

We have not bothered to give our view (self.view) a reasonable frame. This is because we are relying on someone else to frame the view appropriately. In this case, the “someone else” is the window, which responds to having its rootViewController property set to a view controller by framing the view controller’s view appropriately as the root view before putting it into the window as a subview.

share|improve this answer
add comment

The problem with CEarwood's approach is that this is a ViewController, and its view is not the subview of any other view, so calling self.view.subview just results in nil. Remember that the Apple documentation and guidelines strongly suggest that a UIViewController occupies more or less the whole screen (besides the navigation bar or tab bar etc.).

Palimondo's answer is basically the right one: your UIViewController needs to init its view in loadView, but it doesn't need to specify its frame or constraints because those are automatically set to the window's frame and constraints. This is exactly what is done by default if you don't implement loadView yourself.

share|improve this answer
 
There's a little mistake: You are talking about self.view.superview instead of self.view.subview –  Klaas Aug 29 at 14:03
add comment

I'm not sure you need to set the constraints for the root view of the window.

That said, your constraints look correct, I think the exception you get is because this:

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:0 views:views]];

uses the | notation to represent the view's superview. As the root level view, it has no superview. Something like this may work better:

- (void)loadView {
    UIView *customView = [[UIView alloc] init];
    [self.view addSubview:customView];
    NSDictionary* views = @{@"customView" : customView};

    [customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[customView]|" options:0 metrics:0 views:views]];
    [customView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[customView]|" options:0 metrics:0 views:views]];
}
share|improve this answer
 
But I explicitly don't want to add the view as a subview, because then I would have an unused view in between which I don't need. –  Tobias Müller Apr 30 at 11:46
 
How much does having an extra,unused view matter? I'm just asking here, that's a question for you. Have you tried dropping the constraints? What constraints come with a default view controller via the Storyboard? –  James Apr 30 at 12:48
 
The views are created programatically, so nothing from the Storyboard. The extra view wastes cpu time and memory. –  Tobias Müller Apr 30 at 13:12
 
Well, if it matters that much to you then it sounds like you need to skip autolayout for the root view. "The view associated with the window’s root view controller gets a frame based on the characteristics of the window. " - developer.apple.com/library/ios/#featuredarticles/… –  James Apr 30 at 17:32
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.