Your scroll view's subviews need two sets of constraints.
The first set is to dictate the scrolling behavior of the scroll view (i.e. what the scroll view’s contentSize
is). We use the scroll view’s contentLayoutGuide
for this. Note, unlike most constraints, this does not dictate the size of the subviews, but only the relationship between these subview and the scroll view's contentSize
.
The second set is the constraints for the size of the subviews. For this, we use the scroll view’s frameLayoutGuide
.
Thus, assuming that you have three subviews, a red, green, and blue, respectively, you could do:
// Set horizontal constraints relative to scroll view contentLayoutGuide
NSLayoutConstraint.activate([
redView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
greenView.leadingAnchor.constraint(equalTo: redView.trailingAnchor),
blueView.leadingAnchor.constraint(equalTo: greenView.trailingAnchor),
blueView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
])
// set constraints that are common to all three subviews
for subview in [blueView, greenView, redView] {
NSLayoutConstraint.activate([
// Set vertical constraints to scroll view contentLayoutGuide
subview.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
subview.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
// Set width and height of subviews relative to scroll view's frame
subview.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
subview.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor),
])
}
The above assumes that you’ve got translatesAuthresizingMaskIntoConstraints
for these three subviews turned off. But hopefully the above illustrates the idea.
By the way, you can also do this all in IB (referencing the “content layout guide” and “frame layout guide”, just like above). I just did it programmatically here so you can see exactly what’s going on.
Prior to iOS 11, we didn’t have contentLayoutGuide
and frameLayoutGuide
. So when you set constraints between a scroll view and its subviews, it acted like contentLayoutGuide
(i.e. only affected the relationship of the contentSize
and the subviews) and to set the actual size of the subviews, you had to reach to the scroll view’s superview:
// Set horizontal constraints relative to scroll view contentSize
NSLayoutConstraint.activate([
redView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
greenView.leadingAnchor.constraint(equalTo: redView.trailingAnchor),
blueView.leadingAnchor.constraint(equalTo: greenView.trailingAnchor),
blueView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
])
// set constraints that are common to all three subviews
for subview in [blueView, greenView, redView] {
NSLayoutConstraint.activate([
// Set vertical constraints to scroll view contentSize
subview.topAnchor.constraint(equalTo: scrollView.topAnchor),
subview.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
// Set width and height of subviews relative to superview
subview.widthAnchor.constraint(equalTo: view.widthAnchor),
subView.heightAnchor.constraint(equalTo: view.heightAnchor),
])
}
By the way, Apple discusses this behavior in Technical Note TN 2154.
But if targeting iOS 11 and later, use the more intuitive contentLayoutGuide
and frameLayoutGuide
.