title: Autolayout the subviews of a `setFrame:` UIView
body: <p>I have a container <code>UIView</code>. Its children's positions are managed with autolayout. However, I want to position the container itself <strong>explicitly</strong>.</p>
  
  <p>The container exists within a <code>UIView</code> subclass that I call <code>VideoPreviewView</code>. The container is named <code>contentOverlayView</code>.</p>
  
  <p>Here's my code to add an obvious red child to the container that fills it, except for a 1 pixel margin at the edges:</p>
  
  <pre><code>UIView *const redView = [UIView new];
  [redView setTranslatesAutoresizingMaskIntoConstraints: NO];
  [redView setBackgroundColor: [UIColor redColor]];
  [[videoView contentOverlayView] addSubview: redView];
  
  [[videoView contentOverlayView] addConstraints:
   [NSLayoutConstraint
    constraintsWithVisualFormat:@"H:|-(1@900)-[redView]-(1@900)-|"
    options:0
    metrics:nil
    views:NSDictionaryOfVariableBindings(redView)]];
  
  [[videoView contentOverlayView] addConstraints:
   [NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-(1@900)-[redView]-(1@900)-|"
    options:0
    metrics:nil
    views:NSDictionaryOfVariableBindings(redView)]];
  </code></pre>
  
  <h1>Setting frame in <code>layoutSubviews</code></h1>
  
  <p>My initial approach to keeping the container view positioned within was to simply override <code>layoutSubviews</code> for <code>VideoPreviewView</code> and explicitly set the frame of the container there, like this:</p>
  
  <pre><code>-(void) layoutSubviews
  {
    // Other stuff snipped out.
    const CGRect videoBounds = [self videoBounds];
    [_contentOverlayView setFrame: videoBounds];
    [super layoutSubviews];
  }
  </code></pre>
  
  <p>…But this didn't work. If I use <code>recursiveDescription</code> on <code>videoView</code> I get:</p>
  
  <pre><code>&lt;VideoPreviewView: 0x146a54f0; frame = (0 0; 320 460); layer = &lt;CALayer: 0x146a5640&gt;&gt;
     | &lt;UIView: 0x145b6610; frame = (0 0; 2 2); layer = &lt;CALayer: 0x145b6680&gt;&gt;
     |    | &lt;UIView: 0x145bff60; frame = (1 1; 0 0); layer = &lt;CALayer: 0x145bffd0&gt;&gt;
  </code></pre>
  
  <p>It has just enough width to respect the margins I required, but the position is completely wrong, and the frame isn't as large as I want.</p>
  
  <p>I used <code>_autoLayoutTrace</code> on the container view to try to resolve this and I'm told that the layout is ambiguous:</p>
  
  <pre><code>UIWindow:0x145a3a50
  |   UIView:0x145a6e60
  |   |   HPBezelView:0x1468d580
  |   |   |   UIImageView:0x145aab30
  |   |   *VideoPreviewView:0x146a54f0
  |   |   |   *UIView:0x145b6610- AMBIGUOUS LAYOUT for UIView:0x145b6610.minX{id: 5}, UIView:0x145b6610.minY{id: 13}, UIView:0x145b6610.Width{id: 8}, UIView:0x145b6610.Height{id: 16}
  |   |   |   |   *UIView:0x145bff60- AMBIGUOUS LAYOUT for UIView:0x145bff60.minX{id: 4}, UIView:0x145bff60.minY{id: 12}, UIView:0x145bff60.Width{id: 9}, UIView:0x145bff60.Height{id: 17}
  </code></pre>
  
  <p>This suggested to me that if I'm using autolayout, the frame is ignored, so I need to set the container's frame using autolayout too.</p>
  
  <h1>Adding size constraints</h1>
  
  <p>Some other posts and blogs and such also suggest this course of action. So I added some constraints to control the width and height of the container:</p>
  
  <pre><code>_contentOverlayWidthConstraint =
  [NSLayoutConstraint constraintWithItem: contentOverlayView
                               attribute: NSLayoutAttributeWidth
                               relatedBy: NSLayoutRelationEqual
                                  toItem: nil
                               attribute: NSLayoutAttributeNotAnAttribute
                              multiplier: 1.0
                                constant: 1];
  
  _contentOverlayHeightConstraint =
  [NSLayoutConstraint constraintWithItem: contentOverlayView
                               attribute: NSLayoutAttributeHeight
                               relatedBy: NSLayoutRelationEqual
                                  toItem: nil
                               attribute: NSLayoutAttributeNotAnAttribute
                              multiplier: 1.0
                                constant: 1];
  
  
  [contentOverlayView addConstraints: @[_contentOverlayWidthConstraint, _contentOverlayHeightConstraint]];
  </code></pre>
  
  <p>I updated my <code>layoutSubviews</code> method to adjust the "constants" on these constraints:</p>
  
  <pre><code>-(void) layoutSubviews
  {
    const CGRect videoBounds = [self videoBounds];
    [_contentOverlayView setFrame: videoBounds];
    [_contentOverlayWidthConstraint setConstant: CGRectGetWidth(videoBounds)];
    [_contentOverlayHeightConstraint setConstant: CGRectGetHeight(videoBounds)];
  
    [super layoutSubviews];
  }
  </code></pre>
  
  <p>This works a little better – the red view is visible now and has the correct size, but it is still located at the wrong place. I still get ambiguities from the trace:</p>
  
  <pre><code>   UIWindow:0x17d76a00
     |   UIView:0x17d7dfd0
     |   |   HPBezelView:0x17d7d7c0
     |   |   |   UIImageView:0x17d82240
     |   |   *VideoPreviewView:0x17d925e0
     |   |   |   UIView:0x17d96790
     |   |   |   *UIView:0x17d96830- AMBIGUOUS LAYOUT for UIView:0x17d96830.minX{id: 9}, UIView:0x17d96830.minY{id: 16}
     |   |   |   |   *UIView:0x17d9ae90- AMBIGUOUS LAYOUT for UIView:0x17d9ae90.minX{id: 8}, UIView:0x17d9ae90.minY{id: 15}
  </code></pre>
  
  <p>… so, the width and height ambiguities have gone, but the position ambiguities remain.</p>
  
  <h1>Adding position constraints</h1>
  
  <p>So, I added two more layout constraints to set the container's position:</p>
  
  <pre><code>_contentOverlayLeftConstraint =
  [NSLayoutConstraint constraintWithItem: contentOverlayView
                               attribute: NSLayoutAttributeLeft
                               relatedBy: NSLayoutRelationEqual
                                  toItem: nil
                               attribute: NSLayoutAttributeNotAnAttribute
                              multiplier: 1.0
                                constant: 0];
  
  _contentOverlayTopConstraint =
  [NSLayoutConstraint constraintWithItem: contentOverlayView
                               attribute: NSLayoutAttributeTop
                               relatedBy: NSLayoutRelationEqual
                                  toItem: nil
                               attribute: NSLayoutAttributeNotAnAttribute
                              multiplier: 1.0
                                constant: 0];
  
  
  [contentOverlayView addConstraints: @[_contentOverlayLeftConstraint, _contentOverlayTopConstraint]];
  </code></pre>
  
  <p>And updated <code>layoutSubviews</code>: </p>
  
  <pre><code>-(void) layoutSubviews
  {
    [[self contentView] setFrame: [self bounds]];
    [[self videoPreviewLayer] setFrame: [self bounds]];
  
    const CGRect videoBounds = [self videoBounds];
    [_contentOverlayWidthConstraint setConstant: CGRectGetWidth(videoBounds)];
    [_contentOverlayHeightConstraint setConstant: CGRectGetHeight(videoBounds)];
    [_contentOverlayLeftConstraint setConstant: CGRectGetMinX(videoBounds)];
    [_contentOverlayTopConstraint setConstant: CGRectGetMinY(videoBounds)];
  
    [super layoutSubviews];
  }
  </code></pre>
  
  <h1>Crash</h1>
  
  <p>But when I try to add the new constraints, I get an exception with the error:</p>
  
  <blockquote>
    <p><strong>* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*</strong> +[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]: A constraint cannot be made that sets a location equal to a constant. Location attributes must be specified in pairs'</p>
  </blockquote>
  
  <p>This I don't get at all. I can't see how I can rectify this.</p>
  
  <p>Can you help?</p>