I have a a UIViewController
which has a header area. At the bottom of that header element is a row of UIButtons. Below these buttons, I've placed a UIScrollView
, within which is embedded a UIView
called contentContainer
. Within that container is a number of UILabels
and UIViews
styled as dividers.
The entire ViewController, all subviews, and their associated auto-layout constraints are constructed programmatically.
Everything seems to work well, with the exception that the UIScrollView is not vertically scrolling. If I explicitly give the contentContainer
subview a size with an auto-layout height constraint, it works.
This leads me to think that height of the contentContainer is not being accurately computed.
Example of how each UI element is built:
let divider1: UIView = {
let divider = UIView()
divider.backgroundColor = Colors.white10
divider.translatesAutoresizingMaskIntoConstraints = false
return divider
}()
My viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(buttonClose)
self.view.addSubview(labelPlaceTitle)
self.view.addSubview(buttonShare)
self.view.addSubview(buttonRoute)
self.view.addSubview(buttonDelete)
self.view.addSubview(scrollView)
scrollView.addSubview(contentContainer)
contentContainer.addSubview(divider1)
contentContainer.addSubview(labelAddressLabel)
contentContainer.addSubview(labelAddressActual)
contentContainer.addSubview(divider2)
contentContainer.addSubview(labelDateLabel)
contentContainer.addSubview(labelDateActual)
contentContainer.addSubview(divider3)
contentContainer.addSubview(labelNoteLabel)
contentContainer.addSubview(labelNoteActual)
contentContainer.addSubview(buttonAddNote)
contentContainer.addSubview(divider4)
setupLayout()
}
My auto-layout constraints:
private func setupLayout() {
buttonClose.topAnchor.constraint(equalTo: view.topAnchor, constant: 16).isActive = true
buttonClose.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -16).isActive = true
buttonClose.widthAnchor.constraint(equalToConstant: 28).isActive = true
buttonClose.heightAnchor.constraint(equalToConstant: 28).isActive = true
labelPlaceTitle.topAnchor.constraint(equalTo: view.topAnchor, constant: 48).isActive = true
labelPlaceTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
labelPlaceTitle.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -24).isActive = true
buttonShare.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
buttonShare.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24).isActive = true
buttonShare.widthAnchor.constraint(equalToConstant: 48).isActive = true
buttonShare.heightAnchor.constraint(equalToConstant: 48).isActive = true
buttonRoute.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
buttonRoute.leftAnchor.constraint(equalTo: buttonShare.rightAnchor, constant: 8).isActive = true
buttonRoute.widthAnchor.constraint(equalToConstant: 48).isActive = true
buttonRoute.heightAnchor.constraint(equalToConstant: 48).isActive = true
buttonDelete.topAnchor.constraint(equalTo: labelPlaceTitle.bottomAnchor, constant: 16).isActive = true
buttonDelete.leftAnchor.constraint(equalTo: buttonRoute.rightAnchor, constant: 8).isActive = true
buttonDelete.widthAnchor.constraint(equalToConstant: 48).isActive = true
buttonDelete.heightAnchor.constraint(equalToConstant: 48).isActive = true
scrollView.topAnchor.constraint(equalTo: buttonRoute.bottomAnchor, constant: 32).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
contentContainer.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0).isActive = true
contentContainer.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: 0).isActive = true
contentContainer.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 0).isActive = true
contentContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true
contentContainer.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor).isActive = true
contentContainer.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
contentContainer.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
divider1.topAnchor.constraint(equalTo: contentContainer.topAnchor, constant: 24).isActive = true
divider1.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
divider1.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
divider1.heightAnchor.constraint(equalToConstant: 1).isActive = true
labelAddressLabel.topAnchor.constraint(equalTo: divider1.bottomAnchor, constant: 12).isActive = true
labelAddressLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
labelAddressLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
labelAddressActual.topAnchor.constraint(equalTo: labelAddressLabel.bottomAnchor, constant: 4).isActive = true
labelAddressActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
labelAddressActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
divider2.topAnchor.constraint(equalTo: labelAddressActual.bottomAnchor, constant: 12).isActive = true
divider2.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
divider2.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
divider2.heightAnchor.constraint(equalToConstant: 1).isActive = true
labelDateLabel.topAnchor.constraint(equalTo: divider2.bottomAnchor, constant: 12).isActive = true
labelDateLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
labelDateLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
labelDateActual.topAnchor.constraint(equalTo: labelDateLabel.bottomAnchor, constant: 4).isActive = true
labelDateActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
labelDateActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
divider3.topAnchor.constraint(equalTo: labelDateActual.bottomAnchor, constant: 12).isActive = true
divider3.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
divider3.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
divider3.heightAnchor.constraint(equalToConstant: 1).isActive = true
labelNoteLabel.topAnchor.constraint(equalTo: divider3.bottomAnchor, constant: 12).isActive = true
labelNoteLabel.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
labelNoteLabel.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
labelNoteActual.topAnchor.constraint(equalTo: labelNoteLabel.bottomAnchor, constant: 4).isActive = true
labelNoteActual.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
labelNoteActual.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
buttonAddNote.topAnchor.constraint(equalTo: labelNoteActual.bottomAnchor, constant: 12).isActive = true
buttonAddNote.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
buttonAddNote.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
buttonAddNote.heightAnchor.constraint(equalToConstant: 32).isActive = true
divider4.topAnchor.constraint(equalTo: buttonAddNote.bottomAnchor, constant: 12).isActive = true
divider4.leftAnchor.constraint(equalTo: contentContainer.leftAnchor, constant: 24).isActive = true
divider4.rightAnchor.constraint(equalTo: contentContainer.rightAnchor, constant: -24).isActive = true
divider4.heightAnchor.constraint(equalToConstant: 1).isActive = true
}
}
And a shot of the UI, with the light grey portion of the sheet representing the contentContainer
:
Thanks for your help.
In order to compute the height of your contentContainer
, Auto Layout needs an unbroken chain of subviews from the top of contentContainer
to the bottom. In your case, you seem to have that chain except that your bottommost view is not connected to the bottom of the contentContainer
.
Add a constraint connecting the bottom of divider4
to the bottom of contentContainer
. This will allow Auto Layout to compute the size of contentContainer
.
Also, you shouldn't set these constraints:
contentContainer.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor).isActive = true
contentContainer.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true