I have been messing with this code for a while. My UIViews are not showing up after looping through. I have tried this with out constraints, and the result was each of the UIViews were being rendered on top of each other. I would prefer to use the constraints, like I have it set up.
Can you take a look at this code and explain why my UIViews are not rendering and why my UIScrollViews are not scrolling?
import UIKit
class EventsListViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scrollView = UIScrollView()
let scrollViewConstraints: [NSLayoutConstraint] = [
scrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
scrollView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor)
]
self.view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(scrollViewConstraints)
scrollView.backgroundColor = .gray
let stackView = UIStackView(frame: scrollView.frame)
let stackViewConstraints: [NSLayoutConstraint] = [
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor),
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor)
]
scrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(stackViewConstraints)
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .fill
stackView.contentMode = .top
stackView.spacing = 5
for event in location.myEvents! {
let listItem = UIView()
listItem.layer.name = event.eventID
listItem.backgroundColor = .blue
stackView.addArrangedSubview(listItem)
let listItemConstraints: [NSLayoutConstraint] = [
listItem.heightAnchor.constraint(equalToConstant: 35),
listItem.widthAnchor.constraint(equalToConstant: stackView.frame.width)
]
listItem.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(listItemConstraints)
}
// scrollView.contentSize = CGSize(width: Int(view.frame.width), height: (height + spacing * count))
}
}
I have tried the following:
The big issue I see is that you constrain the stack view to the exact size of the scroll view. UIScrollView
has a frameLayoutGuide
and a contentLayoutGuide
. The stack view should be constrained to the contentLayoutGuide
. This will allow the stack view to be sized properly based on the views you put in it and it sets the contentSize
of the scroll view as needed. And it will also allow the stack view to scroll within the scroll view if large enough.
Here is a cleaned up version of your code with a bunch of comments. For testing purposes I simply hardcoded the creation of 20 views since I don't have your data model.
Note the change in distribution
for the stack view. Using equalSpacing
allows each view to have its own height. The height of the stack view will be made as tall as need to account for the total height of all view and the spacing. Using fill
(as you had it) attempts to resize the height of the views to fill the height of the stack view. But you want the views to dictate the height of the stack view, not the other way around.
Note that I remove the width constraint on each view. The stack view will take care of the width due to the alignment
of fill
.
Also note the use of the frameLayoutGuide
and contentLayoutGuide
.
Lastly, the last constraint sets the width of the stack view. This is needed since the views in the stack view do not give the stack view an intrinsic width in this case.
override func viewDidLoad() {
super.viewDidLoad()
let scrollView = UIScrollView()
scrollView.backgroundColor = .gray
scrollView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(scrollView)
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .equalSpacing // let each view have its own height
stackView.alignment = .fill // make each view fill the width of the stack view
stackView.spacing = 5
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stackView)
for _ in 0...20 {
let listItem = UIView()
//listItem.layer.name = event.eventID
listItem.backgroundColor = .blue
listItem.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(listItem)
NSLayoutConstraint.activate([
listItem.heightAnchor.constraint(equalToConstant: 35), // give each view a specific height
// With an alignment of fill, the stack view will make the width of each view its own width
])
}
NSLayoutConstraint.activate([
// Use frameLayoutGuide to constrain the visible portion of the scroll view
scrollView.frameLayoutGuide.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
scrollView.frameLayoutGuide.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
scrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
scrollView.frameLayoutGuide.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
// Use contentLayoutGuide to fit the scroll view's contentSize to the stack view
stackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
stackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
// Give the stack view a fixed width to match the scroll view's frame, or give it some other width as desired
stackView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
])
}