In iOS 11 and later, scroll views have two sets of layout guides, one for the scrollable content, contentLayoutGuide
(which dictates the scrolling behavior), and one for its frame, frameLayoutGuide
(which dictates the size of the subview).
For example, this adds a subview that is inset by 20 points all the way around, whose width is set relative to the frame of the scroll view, but has a fixed height:
let subview = UIView()
subview.translatesAutoresizingMaskIntoConstraints = false
subview.backgroundColor = .red
scrollView.addSubview(subview)
NSLayoutConstraint.activate([
subview.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor, constant: 20),
subview.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: -20),
subview.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor, constant: 20),
subview.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor, constant: -20),
subview.heightAnchor.constraint(equalToConstant: 1000),
subview.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: -40),
])
You can also do this in IB, without any coding at all:
Prior to iOS 11, in the absence of the frameLayoutGuide
, you had to set the subview’s constraints based upon the scroll view’s superview, and I outline that process below. But since iOS 11, the above is the more intuitive solution.
The constraints you described in your question are equivalent to the following VFL:
The scroll view occupies the entire view
H:|[scrollView]|
V:|[scrollView]|
The red view is 60 pt tall and has a margin of 15 to the edges of the scroll view's contentSize
:
H:|-(15)-[redView]-(15)-|
V:|-(15)-[redView(60)]-(15)-|
The red view is ambiguous because there is nothing that defines its width. (The horizontal constraints between the red view and the scroll view define the scroll view's contentSize
, not the red view's width. See Apple Technical Note 2154.)
You resolve this ambiguity by adding a constraint that says that the red view is 30pt narrower than the main view. So add constraint between red view an the scroll view's superview by control-dragging from the red view to the scroll view (and this is probably easiest to do from the document outline):
Then choose "equal widths":
Having defined the red view to be the same width of the main view, you now have to alter that constraint to modify the constant to adjust for the margins you supplied between the red view and the scroll view's contentSize
. Thus, select that constraint you just added and edit it, changing the constant to -30:
Frankly, that Interface Builder technique is a little cumbersome. It may be easier to illustrate how to do this programmatically:
view.addConstraint(NSLayoutConstraint(item: redView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1.0, constant: -30))