title: Autolayout Constraints "disappear" on scroll in UIPageViewController
body: <p>I am trying to build a scroll view with a "floating" header using auto-layout. To be more exact I am trying to build a calendar view with several columns. Each of those column should have its own header which should float on top while the column can be scrolled vertically below it.</p>
  
  <p>If everything works it looks like this:
  <img src="https://i.stack.imgur.com/ZNduG.png" alt="Calendar display working correctly"></p>
  
  <p>As you can see there are several columns and a page control to indicate how many more pages of columns are available.</p>
  
  <p>However when swiping/panning (or even just trying to swipe) to switch to the next page, the constraints keeping the header labels on top are removed and they disappear to the top of the scroll view (where they are in the storyboard). </p>
  
  <p><img src="https://i.stack.imgur.com/t76wi.png" alt="Broken calendar view part 1">
  Column headers are not visible due to being scrolled offscreen</p>
  
  <p><img src="https://i.stack.imgur.com/dKXrb.png" alt="Broken calendar view part 2">
  Column headers are positioned at top of the scroll view (and the height is wrong).</p>
  
  <h2>Setup</h2>
  
  <p>A user can switch between dates ("Today" button at the top with left-right-arrows) and switch between the people displayed. (swipe/pan on view)
  Boh interactions are realized with <code>UIPageViewController</code>s inside one another. The outer page view controller switches between dates, the inner one between the pages of columns (with people).
  The time view is contained in the outer page view controller but not the inner one. </p>
  
  <p>So the hierarchy of views and controllers looks like this:</p>
  
  <pre><code>Calendar PageViewController (the one controlled via buttons in the navigation bar)
  -- Scroll View of Page View Controller
  ---- Team View Controller (with view)
  ------ Header View reference (see screenshot 2)
  ------ Scroll View for vertical scrolling
  -------- Time View (the one on the left)
  -------- People PageViewController (the one controlled by swiping left/right)
  ---------- ScrollView of Page View Controller
  ------------ ViewController for a Single Page (with view)
  --------------(1-n) Container View Controller (several of those, in the example 4 per page)
  ---------------- Column View Controller
  ------------------ Header View (A UIVisualEffectsView with a label)
  ------------------ calendar column view (the one doing the horizontal stripes)
  </code></pre>
  
  <p>Two pin the header views of the individual column view controllers to the top I use a reference view outside of the inner page view controller and outside of the vertical scroll view. I called it <code>Header View reference</code> in the overview above and you can see it quite nicely in the broken example:</p>
  
  <p><img src="https://i.stack.imgur.com/E0LXp.png" alt="Header View Reference"></p>
  
  <p>It's a simple UIVisualEffectsView that I constrained to be at the top left with a fixed height and same width as the time view. So this view has the correct position and height I want all my header views to have and I use that to constraint the individual column header views to it in code (all other constraints are set up in storyboards) in the <code>updateViewConstraints</code> method of each <code>ColumViewController</code> like so:</p>
  
  <pre><code>- (void)updateViewConstraints
  {
      DDLogDebug(@"updating view constraints in colum view controller for %@", self.employee.displayName);
      UIView *baseView = self.headerViewReferenceContainer.viewForHeaderContainerViewConstraints;
      UIView *containerReference = self.headerViewReferenceContainer.headerContainerViewReference;
      NSParameterAssert([containerReference isDescendantOfView:baseView] &amp;&amp; [self.headerContainerView isDescendantOfView:baseView]);
      [baseView addConstraint:[NSLayoutConstraint constraintWithItem:self.headerContainerView
                                                       attribute:NSLayoutAttributeTop
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:containerReference
                                                       attribute:NSLayoutAttributeTop
                                                          multiplier:1.0
                                                            constant:0]];
  
      [baseView addConstraint:[NSLayoutConstraint constraintWithItem:self.headerContainerView
                                                           attribute:NSLayoutAttributeHeight
                                                       relatedBy:NSLayoutRelationEqual
                                                          toItem:containerReference
                                                       attribute:NSLayoutAttributeHeight
                                                          multiplier:1.0
                                                            constant:0]];
  
      [super updateViewConstraints];
  }
  </code></pre>
  
  <p><code>baseView</code> is the view to which the constraints should be added. It must be a superview of both the header reference view and the header view of the column view controller. At the moment this will be the view of the <code>Team View Controller</code> above (the one containing the vertical scroll view).</p>
  
  <p><code>containerReference</code> is the Header View Reference view as seen in the screenshot above.</p>
  
  <p>So I constrain the column header views to have the same top position and same height as the reference view. (the width and x position depends on the column)</p>
  
  <p>As you can see in the broken screenshots the Header View reference is always positioned correctly. Also, on loading the view controllers and there views, the constraints are setup correctly. </p>
  
  <p>Then when starting a pan gesture which switches the page of the PeoplePageView Controller and loads the next <code>ViewController for a Single Page</code> the constraints somehow disappear and the header views move to the top of the scroll view due to no longer being pinned to the top. </p>
  
  <p>The next <code>View Controller for a Single Page</code> has the constraints set up correctly, but only until it snaps into place.</p>
  
  <p><img src="https://i.stack.imgur.com/PsiAY.png" alt="While Panning"></p>
  
  <p>So while panning the constraints of the view controller to display are setup correctly. But as soon as it snaps into place (feels like it happens at <code>-viewDidAppear</code>) the constraints are removed as well.</p>
  
  <p><strong>I don't understand why and how the constraints are removed.</strong></p>
  
  <h2>What is not wrong / what I tried</h2>
  
  <p>It could be due to one of the related views disappearing, but</p>
  
  <ul>
  <li>the <code>baseView</code> doesn't change at all on swiping left right as it is a superview of the paging scroll view</li>
  <li>the header reference view is not changed as it is not contained in the paging Page View Controller.</li>
  <li>the header column view does not disappear until the view controller is fully offscreen, so the layout should not break during the paging</li>
  </ul>
  
  <p>I used to have an issue with the layout only falling into place AFTER the paging finished. This was due to my constraints relating to the top layout constraints as described in <a href="https://stackoverflow.com/questions/25791183/ios-8-uipageviewcontroller-applying-constraints-after-transitions">iOS 8 UIPageViewController Applying Constraints After Transitions</a>.
  As a result I use a fixed distance from the super view's top to pin my <code>Header View Reference</code> instead of pinning it to the top layout constraint.</p>
  
  <p>So the bug of the UIPageViewController not having a correct top layout constraint on paging shouldn't be the issue either.</p>
  
  <p>I do not add any views programatically. All my Storyboards use auto layout and view controller are added to each other via Container View Controllers in the Storyboard or implementing a <code>UIPageViewControllerDatasource</code> and instantiating the view controller from a xib. So there shouldn't be any issues related to <code>translatesAutoResizingMasks</code>. Also, it works on load, it only breaks on paging, this doesn't fit the usual <code>translatesAutoResizingMasks = YES</code> problem.</p>
  
  <p>I also do not get any errors about conflicting constraints or the view hierarchy not being prepared for constraints, the working constraints are simply removed.</p>
  
  <p>I due have one issue which I think is unrelated, but I'll list it here for completeness' sake: I get an error due to the layout constraints of the labels <strong>inside</strong> the column header views being added to early:</p>
  
  <pre><code>The view hierarchy is not prepared for the constraint: &lt;NSLayoutConstraint:0x7af4df20 UILabel:0x7af4e0d0'Susanne'.width == UIVisualEffectView:0x7af4de70.width&gt;
  When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
  </code></pre>
  
  <p>However, that message complains about the relation between the label and its containing view (the header view of the column), not about the "outer" constraints of the header view. Also, it warns that the app will crash if the constraints need to be resolved too early. The app does not crash, so the timing seems to be right. Also, the constraints the app complains about are setup in a storyboard, so I don't see how I could get the timing wrong.</p>
  
  <p>tl;dr: Constraints that reach through several levels of the view hierarchy from inside a UIPageViewController to a view outside of it are removed (without warning, comment, reason or anything). 
  I don't understand why and how to prevent that from happening or at what point to add them again.</p>
  
  <p><strong>UPDATE:</strong></p>
  
  <p>I added the following call to <code>viewWillAppear:</code>:</p>
  
  <pre><code>[self.view setNeedsUpdateConstraints]
  </code></pre>
  
  <p>This has the following effect: </p>
  
  <ul>
  <li>When dragging the page to the side a bit (not switching the paging) and releasing it again (so it snaps back) the broken constraints are repaired again (and the layout is fixed). This is due to the page view controller calling <code>viewWillAppear:</code> on the original view controller if it "snaps back" (to offset the previously issued <code>viewWillDisappear</code> call).</li>
  <li>But during the scrolling the layout is still broken.</li>
  <li>Also when swiping to a new page, the constraints of the new page are also broken.</li>
  <li>But whenever <code>viewWillAppear</code> is called the constraints are fixed for a short time until they are removed again (usually when the scroll ends).</li>
  </ul>
  
  <p>If I add the same call to <code>viewDidAppear</code> <strong>instead of</strong> <code>viewWillAppear</code> the following happens:</p>
  
  <ul>
  <li>This leads to the layout being correct most of the time (they are fixed after scrolling ends). </li>
  <li>But it is still broken during scrolling.</li>
  </ul>
  
  <p>Now if I add <code>[self.view setNeedsUpdateConstraints]</code> to both methods the following happens:</p>
  
  <ul>
  <li>The layout behaves correctly almost all of the time, no strange jumping around of views</li>
  <li><strong>Except</strong> for the first page change. During that one the constraints of the original page seem to be removed and the layout is broken in the way shown above. After that, the constraints seem to be fixed permanently and swiping back and forth works without issues.</li>
  <li>This is even true for three pages (I also tried it with four) when paging to the last page (and trying to go beyond it) and back although I'm pretty sure the page view controller does not keep all three pages in memory.</li>
  </ul>